Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Control.Lens lens for a total map

I have a total map from type A to type B.

import qualified Data.Map as M
import Data.Maybe

tmGet k m = fromJust $ M.lookup k m

tmSet k v = M.insert k v

I used Data.Map as an example implementation but it can be anything, e.g. an Array or even a Bool-indexed tuple:

tmGet True  = fst
tmGet False = snd

I want to have a tmAt function to construct a lens:

(42, 665) ^. tmAt True == 42
(42, 665) & tmAt False +~ 1 == (42, 666)

The question is how do I construct tmAt out of tmGet and tmSet (or tmModify)?

like image 892
nponeccop Avatar asked Oct 20 '22 22:10

nponeccop


1 Answers

If you look at the documentation for the Control.Lens module there's a very handy image of different parts of the lens package. Since you want to construct a Lens, you can look at the Lens part of the diagram. The topmost function shown is

lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b

This constructs a Lens from a getter and a setter.

The function s -> a is a getter – the type signature means, "If you give me a data structure s, I will pick an a value out of it." The s -> b -> t function is the setter, and the type signature means, "If you give me an s and a new value b, I will create for you a new structure t." (The types are different because lenses can actually change types of things.)

If your getter is tmGet and your setter is tmSet, you can therefore construct a lens with

tmAt :: Boolean -> Lens s t a b
tmAt b = lens (tmGet b) (tmSet b)

for whatever your actual s, t, a, and b parameters are. In the example of a tuple, it would be

tmAt :: Bool -> Lens (a, a) (a, a) a a

(In other words, if you give the Lens a function a -> a, it can transform an (a, a)-tuple into another (a, a)-tuple.)


If you want to be fancy, you can also rewrite tmAt as

tmAt = lens <$> tmGet <*> tmSet
like image 113
kqr Avatar answered Oct 24 '22 16:10

kqr