aula
aula copied to clipboard
Add initial lens faq.
not sure about the title of the older question; if somebody has a better idea...
@np: answer and context to first question missing. thanks! (-:
Here are a few hints at using lenses based on examples.
modifyDb_ :: AulaSetter a -> (a -> a) -> AUpdate ()
modifyDb_ l f = AUpdate . ExceptT . fmap Right $ modify (l %~ f)
First there is no need to wrap the AUpdate stack ourself, this works: modifyDb_ l f = modify (l %~ f)
.
Then instead of modify (l %~ f)
one can use l %= f
the mnemonic is that ~
is for functional update and =
for MonadState
updates.
modifyDb :: AulaLens a -> (a -> a) -> AUpdate a
modifyDb l f = AUpdate . ExceptT . fmap Right $ state (\s -> (f $ s ^. l, l %~ f $ s))
The first remark still applies, secondly this kind of update which also returns the computed result is called a «pass-through» update in lens. The symbol for that in lens is <
, so:
-
s & l <+~ 1
returns both the new targeted value and the full updated value:(s ^. l + 1, s & l +~ 1)
. -
l <+= 1
increments the state by 1 at locationl
and returns new targeted value:do { x <- view l; l += 1; pure (x + 1) }
.
Some more hints:
-
l .~ Just x
becomesl ?~ x
. -
l .= Just x
becomesl ?= x
. -
l1 %~ (l2 .~ x)
becomesl1 . l2 .~ x
. -
l1 %~ (l2 %~ f)
becomesl1 . l2 %~ f
. -
l %~ const x
becomesl .~ x
. -
s & l .~ x
tends to be more readable thanl .~ x $ s
-
id .~ x
becomesconst x
-
id .= x
becomesput x
-
id %~ f
becomesf
-
id %= f
becomesmodify f
-
(l .= x) >> pure x
becomesl <.= x
also we should make a list of good resources:
- spj's skills matter talk
- kmett's boston meetup marathon
- ...?
- reading material?
questions from #342:
[...]
<$> ("email" .: (email & prelens emailAddress %%~ DF.optionalText))
[...]
"email" -> pure [TextInput $ email ^. _Just . re emailAddress]
[...]
<*> ("desc" .: ((topic ^. topicDesc) & _Markdown %%~ (DF.text . Just)))
[...]
("email" .: (email & prelens emailAddress %%~ DF.optionalText))
The prelens
function is gone now and there is a comment about it.
"email" -> pure [TextInput $ email ^. _Just . re emailAddress]
The doc for emailAddress
addresses that.
("desc" .: ((topic ^. topicDesc) & _Markdown %%~ (DF.text . Just)))
Let's abstract (topic ^. topicDesc)
as s
and (DF.text . Just)
as f
. We get: s & _Markdown %%~ f
.
The operator %%~
is like %~
but works with effectful functions (a -> f b
). So this is equivalent to unwrapping the Markdown constructor calling f
and wrapping the constructor back again.
[copied from https://github.com/liqd/aula/pull/360#discussion_r59679339]
instead of
maybe "" id pw
you can write:
pw ^. _Just
This works because when ^. is applied to a Prism it goes down to a Fold and get this type: (^.) :: Monoid a => s -> Fold s a -> a. In our case (^.) :: Maybe ST -> Fold (Maybe ST) ST -> ST which means that the monoid instance for ST is doing the work of defaulting to the empty string.
[irc]
14:57 < npou> fisx_: anyOf is like any but instead of working directly on the
containers it applies a lens first
[irc]
15:13 < fisx_> npou: is there a type synonym for this?:
15:13 < fisx_> (Monoid (f User), Functor f, Applicative f, Contravariant f) =>
(ST -> f ST) -> User -> f User
15:52 < npou> fisx_: the good one is called Getting, the trick was to look at
the type sig of anyOf, it takes a `Getting Any s a`.
15:54 < npou> so searchee can be either: `Getting Any User ST` or more generally
`Monoid r => Getting r User ST`.
[irc]
15:57 < npou> While Fold is defined as `type Fold s a = forall f. (Contravariant f,
Applicative f) => (a -> f a) -> s -> f s`, the docs gives a simpler
version: `type Fold s a = forall m. Monoid m => Getting m s a`
15:59 < npou> correspondingly Getter is equivalent to `type Getter s a = forall r.
Getting r s a`, which means that we know nothing about `r` hence we can't
use it as a monoid
16:00 < npou> So Getting is to keep in mind when using lenses with monoids
16:01 < npou> Also often `r` is equal to `a` as in the type of `view :: MonadReader s m
=> Getting a s a -> m a`
16:02 < npou> or `(^.) :: s -> Getting a s a -> a`
16:02 * npou tries to get a better grasp at Getting by explaining it...
16:04 < npou> the catch is that searchee ought to be a `Fold User ST` following the
"definition" of Fold above.
https://en.wikibooks.org/wiki/Haskell/Lenses_and_functional_references#The_scenic_route_to_lenses