Good Morning!
mΓ₯ning!
π
Good morning
Morn'
morning
morning
did some more work on bind
/ catch
equivalence @alex.lynham... turns out that if you sub reject
for return
and catch
for bind
then reject
+`catch` follow the 3 monad laws like return
+`bind` ... which makes sense if you think about it - because in an either
context reject
+`catch` are processing the left
branch and short-circuiting the right
branch, whereas return
+`bind` are processing the right
branch and short-circuiting the left
branch. haven't quite figured how finally
fits into the picture yet
that makes sense
isn't finally in that context a fold over the tagged union back into a type that only contains a scalar?
as in, if you have like a ReaderEither
eventually you'll fold it down to a Reader
to return a single value
where the Reader
is the product of either left or right of the ReaderEither
so finally
is just a function that lifts into the Reader
context in the fold action and helps deliver that final type?
no, finally
is a bit odd - it's got a similar signature to bind
, but throws away the result of the fn like m a -> (a -> m b) -> m a
while preserving other effects (like writer log, state etc)
right, but a closure with finally in it will return a value
yes, but always the same value that was put into it, augmented with any effects that were output in the finally block
yeah
it's straightforward to implement, but i haven't figured out if and how it follows any of the associativity laws yet
so it's like identity + side effects
zakly
cos the side effects can only really work on the monadic context i guess
hm
need to ponder further
yeah, i'm in ponderland
is there any reason the finally block couldn't accept a binding that operated on the wrapped type passed to the finally block?
i know that maybe i'm a little obsessed with the idea it's just a fold and a lift
Btw @mccraigmccraig, I've added some more detail to our convo as you asked about it, not sure if you have anything to add based on it, if not thanks for your help π...
oh, my bad @folcon - yesterday rather got away with me π¬
@alex.lynham the finally fn shouldn't need lifting - it's a regular monadic fn so e.g.
user> (m/with-context rwx/rwexception-ctx
(-> (m/return "foo")
(m/bind (fn [s] (m/return (str s "bar"))))
(m/bind (fn [v] (throw (ex-info "boo" {:v v}))))
(err/finally (fn [] (w/tell [:finally :happened])))
(err/catch (fn [e] (m/return (ex-data e))))
(r/run {})))
{:monad.writer/output {:finally [:happened]}, :monad/val {:v "foobar"}}
oh i meant more in the sense of conceptually mapping it onto fold. probably not easy to get across what i was getting at over text π i see what you're getting at here though
i think i'm aiming towards a re-frame like programming model, with error handling and fx delivery uniform for both sync and async computations
using monads or an effect type?
using a writer monad with an effect-accumulation monoid
basically just a map of {effect [effect-val]}
so you can just reduce the effects at will, regardless of whether it's sync or async?
looks suspiciously like a variant type π
no, the map can hold many effect descriptions
why a map?
so {:push [{:token "abc" :message "hey"}] :websocket [{:channel "/users/123" :data {...}}]}
could equally be a list of variants though
seems like the list would be more in keeping with a monoidal interface intuitively? just cos i tend to think of maps as being extendable
...unless that is the intention here
i thought the map of lists was cute π¬... it's just a monoid interface impl though, it's easy to switch out
most of yapster's api and routing function comes down to (using re-frame terminology) receive-event -> fetch-cofx -> process -> write-fx
type processes - and currently fetch-cofx
, process
and write-fx
are all mixed up - i'd like a programming model which doesn't require insane amounts of discipline to keep them nicely separated
fair
we have two similar things in current codebase
one is xfm data -> write data -> get db driver return -> xfm that into a serialised form
and it is broken down into sorta two generics xfm -> write db driver return -> xfm which can be turned into typed generics where you pass in compatible fns that you want and then you can combine the two inner combinators to an outer one so for most entities you're usually using v similar legos in a v similar order
so we've ended up just abstracting and abstracting, but that has relied on a disciplined effort
it would be easy for a future dev team to completely trip over it if they weren't disciplined, it would get verbose fast
xfm ?
transformation
think i might even have pinched that shorthand from hickey
that was my first guess π then i realised i wasn't sure
i can tell i've finally snapped after a long winter when i'm writing test fixtures like
{
groupName: 'badger club',
bio: 'the first rule of badger club is DO NOT TALK ABOUT BADGER CLUB'
...
}
spring can't come soon enough
Oh, that doesn't just work? :(....
Ahh...