Record Syntax, Lenses and Prisms: Part 3 - Prisms
Prisms
So what are prisms?
Prisms are like lenses just for sum types … - the most memorable sentence, but I don’t think very helpful at first glance.
So let us start with defining a sum type.
> data Town = Prague | Vienna | Kingston | London | Hipsterhausen deriving (Show, Eq)
> data Pirate = Captain { _attributes :: Human, _ship :: String}
> | FirstMate { _attributes :: Human, _shanty :: String}
> | Marauder { _attributes :: Human, _hometown :: Town}
> makeLenses ''Human
> makeLenses ''Body
> makeLenses ''Pirate
Pirate is a sum type - with different records in each constructor.
A tangent on sums and products
Sums
Why is the above called a sum type, just let us calculate the number of options we have in a sum type:
If I have a variable x :: Sum
it has exactly three values it can be
x = One
x = Two
x = Three
so the sum of the number of constructors is the solution of how many options x
can be.
Product
So how come product types to their names - you might already guess it.
For a variable y :: Product
we find y
can be |Sum|×|Sum|
many possible values:
y = P One One
y = P One Two
y = P One Three
y = P Two One
y = P Two Two
y = P Two Three
y = P Three One
y = P Three Two
y = P Three Three
So can anybody find what exponential types are?
The answer is functions
So the functions from Two -> THREE
have exactly |Two|^|THREE|
many elements.
one,two,three,four,five,six,seven,eight :: Two -> THREE
one One = ONE
one Two = ONE
two One = TWO
two Two = TWO
three One = THREE
three Two = THREE
four One = ONE
four Two = TWO
five One = ONE
five Two = THREE
six One = TWO
six Two = THREE
seven One = THREE
seven Two = ONE
eight One = THREE
eight Two = TWO
Back to Prisms
The problem with lenses is that cpt
some getters don’t make sense. For datatypes that have a special elements, so called monoids like String
with ""
we get special getters, but for Town
for example …
Prelude > cpt^.shanty
""
Prelude > cpt^.hometown
...
error message complaining about Town not being a monoid
...
So an easy way to get special elements is combining it with Maybe
. And that’s what prisms are - getters with Maybe
-values instead of errors.
So how do we get them - again as with prisms we (have to) build them with template haskell magic using
This magic words create “Constructors” _Captain
, _FirstMate
and _Marauder
and the lens library provides functions preview
, review
and ^?
.
Now what are those functions doing - they select one branch in a sum type.
Prelude > preview _Captain cpt
Just (Attributes {...},"SS Sea Serpent")
Prelude > preview _Captain mrd
Nothing
But there is also an infix shortcut for preview
- (^?)
so we could have written the above cpt^._Captain
or use it with accessor-like functions as hometown
,ship
,shanty
or attributes
.
Prelude > cpt^?hometown
Nothing
Prelude > cpt^?ship
Just "SS Sea Serpent"
Prelude > mrd^?hometown
Just Hipsterhausen
So what about review
?
The review
function can create new things from the results of preview
, well not exactly but almost and with use of (<$>)
from Control.Applicative
we can make them work together
Prelude> :t preview _Captain cpt
preview _Captain cpt :: Maybe (Human, String)
Prelude> let Just x = preview _Captain cpt
Prelude> :t review _Captain x
review _Captain x :: Pirate
Prelude> :t review _Captain <$> preview _Captain cpt
review _Captain <$> preview _Captain cpt :: Maybe Pirate
Prelude> review _Captain <$> preview _Captain cpt
Just Captain {Attributes {...},"SS Sea Serpent"}
Prelude> review _Captain <$> preview _Captain mrd
Nothing
Prelude> review _Captain <$> preview _Marauder mrd
... Error ... -- Captain needs String (ship name) where Marauder has a Town as
a second argument
So Prisms do not fix everything - but provide a safety layer for simple accessing stuff and sometimes for generating stuff as well.
So thats all I know about Lenses and Prisms - for understanding the type signatures - I still do not feel confident to present about.