-
Nouveau type et lentilles dans Haskell
2016-08-20
SourceDéfinir un nouveau type dans Haskell
On utilise
datapour 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
reverseBazdé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 typeFoo 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
Foone 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 ''FooCela crée deux nouvelles lentilles,
baretbazqui, respectivement, se concentrent sur le champbaret le champbaz. 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
reverseBaza pu être définie de façon bien plus commode.
