Skip to content

Commit b0c5ac8

Browse files
committed
Implement advanced Haskell lenses
1 parent c133fbb commit b0c5ac8

File tree

6 files changed

+187
-0
lines changed

6 files changed

+187
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
node_modules
22
*.log
33
.DS_Store
4+
*.hi
5+
*.o
6+
*.dyn_hi
7+
*.dyn_o

Haskell/advanced/1-lens.hs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
-- cabal install template-haskell
2+
--
3+
-- Lens is implementation of most common of optics.
4+
--
5+
-- Real makeLens implementation more complex supporting all of Haskell syntax
6+
-- cases for creating types.
7+
--
8+
{-# LANGUAGE Rank2Types #-}
9+
{-# LANGUAGE TemplateHaskell #-}
10+
module Lens where
11+
12+
import Language.Haskell.TH
13+
14+
type Lens s t a b = forall f . Functor f => (a -> f b) -> s -> f t
15+
type Lens' s a = Lens s s a a
16+
17+
lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b
18+
lens getter setter = \afb s -> setter s <$> afb (getter s)
19+
20+
fieldLens :: Name -> (Name, a, Type) -> Q [Dec]
21+
fieldLens s (v, _, a) = do
22+
sVar <- newName "s"
23+
bVar <- newName "b"
24+
let setter = return $ LamE [VarP sVar, VarP bVar] $ RecUpdE (VarE sVar) [(v, VarE bVar)]
25+
body <- NormalB <$> [| lens $(return $ VarE v) $setter |]
26+
let fn = mkName $ tail $ nameBase v
27+
return [ SigD fn (AppT (AppT (ConT $ mkName "Lens'") (ConT s)) a)
28+
, FunD fn [Clause [] body []]
29+
]
30+
31+
makeLens :: Name -> Q [Dec]
32+
makeLens s = do
33+
(TyConI (DataD _ _ _ _ [RecC _ cs] _)) <- reify s
34+
concat <$> mapM (fieldLens s) cs

Haskell/advanced/2-view.hs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--
2+
-- cabal install template-haskell
3+
-- ghc 1-lens.hs 2-view.hs -o 2-view
4+
-- ./2-view
5+
--
6+
{-# LANGUAGE Rank2Types #-}
7+
{-# LANGUAGE TemplateHaskell #-}
8+
import Data.Functor.Const (Const(..))
9+
import Text.Printf (printf)
10+
11+
import Lens
12+
13+
view :: Lens s t a b -> s -> a
14+
view l s = getConst $ l Const s
15+
16+
data City = City
17+
{ _cityName :: String
18+
, _country :: String
19+
, _inEU :: Bool
20+
} deriving Show
21+
$(makeLens ''City)
22+
23+
data Person = Person
24+
{ _name :: String
25+
, _city :: City
26+
, _born :: Int
27+
} deriving Show
28+
$(makeLens ''Person)
29+
30+
person = Person "Marcus Aurelius" (City "Rome" "Italy" True) 121
31+
32+
main = printf "view country of the city person was born in: %v\n"
33+
$ view (city . country) person

Haskell/advanced/3-set.hs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--
2+
-- cabal install template-haskell
3+
-- ghc 1-lens.hs 3-set.hs -o 3-set
4+
-- ./3-set
5+
--
6+
{-# LANGUAGE Rank2Types #-}
7+
{-# LANGUAGE TemplateHaskell #-}
8+
import Data.Functor.Identity (Identity(..))
9+
import Text.Printf (printf)
10+
11+
import Lens
12+
13+
set :: Lens' s a -> a -> s -> s
14+
set l a s = runIdentity $ l (Identity . const a) s
15+
16+
data City = City
17+
{ _cityName :: String
18+
, _country :: String
19+
, _inEU :: Bool
20+
} deriving Show
21+
$(makeLens ''City)
22+
23+
data Person = Person
24+
{ _name :: String
25+
, _city :: City
26+
, _born :: Int
27+
} deriving Show
28+
$(makeLens ''Person)
29+
30+
person = Person "Marcus Aurelius" (City "Rome" "Italy" True) 121
31+
32+
main = printf "set inEU of the city person was born in: %v\n"
33+
$ show $ set (city . inEU) False person

Haskell/advanced/4-over.hs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--
2+
-- cabal install template-haskell
3+
-- ghc 1-lens.hs 4-over.hs -o 4-over
4+
-- ./4-over
5+
--
6+
{-# LANGUAGE Rank2Types #-}
7+
{-# LANGUAGE TemplateHaskell #-}
8+
9+
import Data.Char (toUpper)
10+
import Data.Functor.Identity (Identity(..))
11+
import Text.Printf (printf)
12+
13+
import Lens
14+
15+
over :: Lens' s a -> (a -> a) -> s -> s
16+
over l ab s = runIdentity $ l (Identity . ab) s
17+
18+
data City = City
19+
{ _cityName :: String
20+
, _country :: String
21+
, _inEU :: Bool
22+
} deriving Show
23+
$(makeLens ''City)
24+
25+
data Person = Person
26+
{ _name :: String
27+
, _city :: City
28+
, _born :: Int
29+
} deriving Show
30+
$(makeLens ''Person)
31+
32+
person = Person "Marcus Aurelius" (City "Rome" "Italy" True) 121
33+
34+
main = printf "over name of the city person was born in: %v\n"
35+
$ show $ over (city . cityName) (map toUpper) person

Haskell/advanced/5-together.hs

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
--
2+
-- cabal install template-haskell
3+
-- ghc 1-lens.hs 5-together.hs -o 5-together
4+
-- ./5-together
5+
--
6+
{-# LANGUAGE Rank2Types #-}
7+
{-# LANGUAGE TemplateHaskell #-}
8+
9+
import Data.Char (toUpper)
10+
import Data.Functor.Const (Const(..))
11+
import Data.Functor.Identity (Identity(..))
12+
import Text.Printf (printf)
13+
14+
import Lens
15+
16+
view :: Lens s t a b -> s -> a
17+
view l s = getConst $ l Const s
18+
19+
set :: Lens' s a -> a -> s -> s
20+
set l a s = runIdentity $ l (Identity . const a) s
21+
22+
over :: Lens' s a -> (a -> a) -> s -> s
23+
over l ab s = runIdentity $ l (Identity . ab) s
24+
25+
data City = City
26+
{ _cityName :: String
27+
, _country :: String
28+
, _inEU :: Bool
29+
} deriving Show
30+
$(makeLens ''City)
31+
32+
data Person = Person
33+
{ _name :: String
34+
, _city :: City
35+
, _born :: Int
36+
} deriving Show
37+
$(makeLens ''Person)
38+
39+
person = Person "Marcus Aurelius" (City "Rome" "Italy" True) 121
40+
41+
42+
main = do
43+
printf "view country of the city person was born in: %v\n"
44+
$ view (city . country) person
45+
printf "set inEU of the city person was born in: %v\n"
46+
$ show $ set (city . inEU) False person
47+
printf "over name of the city person was born in: %v\n"
48+
$ show $ over (city . cityName) (map toUpper) person

0 commit comments

Comments
 (0)