--- title: "Nouveau type et lentilles dans Haskell" date: "2016-08-20" output: html_document --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) ``` ## Définir un nouveau type dans Haskell On utilise `data` pour définir un nouveau type dans Haskell. Quelques exemples élémentaires sont donnés ci-dessous.
> -- ##~~ define a new type ~~## --
> 
> -- #~ a new type with unnamed fields ~# -- 
> data Point = Point Float Float deriving (Show)
> -- variable of type Point:
> let point = (Point 3 4)
> point
Point 3.0 4.0
> :t point
point :: Point
> -- 'Point' is like a function:
> :t Point
Point :: Float -> Float -> Point
> -- function acting on 'Point' variables:
> :{
> let squareNorm :: Point -> Float
>     squareNorm (Point x y) = x^2+y^2
> :}
> squareNorm point
25.0
> 
> -- #~ a new type with named fields ~# -- 
> data NamedPoint = NamedPoint{ _x :: Float, _y :: Float } deriving (Show)
> let point = (NamedPoint 3 4) 
> point
NamedPoint {_x = 3.0, _y = 4.0}
> -- we can set the field values by their name:
> NamedPoint { _y = 0, _x = 1 }
NamedPoint {_x = 1.0, _y = 0.0}
> -- get the value of a field:
> _x point 
3.0
> -- update a field (this creates a new 'NamedPoint'):
> point { _y = 5 }
NamedPoint {_x = 3.0, _y = 5.0}
> 
> -- #~ field with unpredefined type ~# --
> data Foo a = Foo{ _bar :: Int, _baz :: a } deriving (Show)
> let w = Foo { _bar = 1, _baz = "hello" }
> w
Foo {_bar = 1, _baz = "hello"}
> :t w
w :: Foo [Char]
> -- function acting on 'Foo String':
> :{
> let reverseBaz :: Foo String -> Foo String
>     reverseBaz (Foo bar baz) = Foo { _bar = bar, _baz = reverse baz }
> :}
> reverseBaz w
Foo {_bar = 1, _baz = "olleh"}
La fonction `reverseBaz` définie dans le code ci-dessus a pour action de renverser la chaîne de caractères contenue dans le deuxième champ d'un objet de type `Foo String` (`= Foo [Char]`). Elle n'agit pas sur le premier champ, et cependant avons dû recopier ce champ : Foo { _bar = bar, _baz = reverse baz } Dans ce cas où la classe `Foo` ne contient que deux champs, ce n'est pas très dérangeant. Mais ce serait plus problématique s'il y avait plus de champs. Une méthode permettant d'agir sur un champ sans avoir besoin de recopier les autres est souhaitable. ## Lentilles Pour cela, on utilise des *lentilles*, de la librairie `lens`. Ci-dessous, des exemples de la lentille `_2`, la lentille qui se concentre sur la 2ème composante d'une paire :
> -- #~ lens example: _2 ~# --
> import Control.Lens
> let a = ("hello", 0::Int)
> -- to get the 2nd element:
> view _2 a 
0
> -- more concisely:
> a ^. _2
0
> -- to set a value to the 2nd element:
> set _2 "world" a
("hello","world")
> -- more concisely:
> _2 .~ "world" $ a
("hello","world")
> -- to apply a function 
> over _2 (+1) $ a
("hello",1)
## Créer ses lentilles On crée des lentilles de la façon suivante :
-- NewType_makeLenses.hs
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens hiding (element)
data Foo a = Foo{ _bar :: Int, _baz :: a } deriving (Show)
makeLenses ''Foo
Cela crée deux nouvelles lentilles, `bar` et `baz` qui, respectivement, se concentrent sur le champ `bar` et le champ `baz`. On les utilise alors comme la lentille `_2`.
> -- load the file creating the lenses
> :l NewType_makeLenses
> 
> -- the makeLenses function has defined new lenses:
> :i bar
bar :: Lens' (Foo a0) Int 	-- Defined at NewType_makeLenses.hs:8:1
> :i baz
baz :: Lens (Foo a0) (Foo a1) a0 a1 	-- Defined at NewType_makeLenses.hs:8:1
> 
> -- they can be used as any other lens:
> let x = Foo { _bar = 1, _baz = "hello" }
> x ^. bar
1
> baz .~ "hi" $ x 
Foo {_bar = 1, _baz = "hi"}
> 
> -- the 'reverseBaz' function again: 
> :{
> let reverseBaz :: Foo String -> Foo String
>     reverseBaz = over baz reverse
> :}
> reverseBaz x
Foo {_bar = 1, _baz = "olleh"}
Ainsi, la fonction `reverseBaz` a pu être définie de façon bien plus commode. ## De la lecture sur les lentilles - https://hackage.haskell.org/package/lens-tutorial-1.0.1/docs/Control-Lens-Tutorial.html - https://hackage.haskell.org/package/lens - https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/a-little-lens-starter-tutorial - http://lens.github.io/