Closed Dark-Ethereal closed 4 years ago
Yes, this is to allow chains with (#)
. The main advantage is that we can create elements and assign values to their properties without binding them to a variable. Example:
getBody #+
[ string "Click the following"
, UI.button # sink UI.text (show <$> val) # set UI.align "left"
]
Here, we do not need to give a name to the button.
The flip side is that whenever we do have a name, e.g. myButton :: Element
, then we have to wrap it in element
(aka return
) in order to chain it.
I believe this tradeoff is worth it, but I am open to discussion. How would you formulate the example above in your approach?
I'm very much confused by this:
sink takes an
mx
... bindsx
tomx
, thenreturn
s anx
completely unchanged! It's just returning the value it was given as an argument!What's the point in returning
x
at all?In fact, come to think of it, why does
sink
need to take aUI
action that returns anx
if all it is going to do is use>>=
(hidden in do notation to apply it to a function taking justx
?Why isn't it sink defined thus:
We can see the same pattern on
set
: it's taking aUI x
to make aUI x
but looking at the insides, clearly it only needs ax
to make aUI ()
#+
also probably needs the same treatment.This would get rid of the need for pointlessly wrapping values with
element
(which is justreturn
) just so we could have sink unwrap them inside with>>=
It makes no sense to use do notation to unwrap a
UI Element
so we can use it with something that takes just anElement
, and then feed it toreturn
/element
to make it aUI Element
just so we can feed it to something that needed aUI Element
in the first place.Yet this superflous wrapping and unwrapping is the way all the example code is structured.
ReaderT It!
I'm pretty sure the reason it's being done is so you can chain things like so:
But this is the wrong solution (IMO).
sink UI.text (show <$> val)
andset UI.align "left"
both need the same element to act upon...sounds like a reader pattern.... but then they make (or should make IMO) a
UI ()
action that needs to be combined with the>>
according toUI
'sMonad
instance...That's a
ReaderT
! We could massage the definition ofset
andsink
so they returnReaderT x UI ()
.We could then define
onElement = runReaderT
then
becomes
Which is perfectly sensible: just like
IORef
, we're separating making the reference from the action takes the reference and makes an action that changes what it refers to.Obviously you could take this further by hiding the use of ReaderT through newtypes or type synonyms so that
sink UI.text (show <$> val)
has typeElementModifier
or something andonElement
has typeElement -> ElementModifier -> UI ()