Fork me on GitHub
#clojure-uk
<
2021-02-23
>
dharrigan07:02:14

Good Morning!

jiriknesl09:02:38

Good morning

mccraigmccraig11:02:55

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

🤯 3
alexlynham11:02:41

that makes sense

alexlynham11:02:20

isn't finally in that context a fold over the tagged union back into a type that only contains a scalar?

alexlynham11:02:58

as in, if you have like a ReaderEither eventually you'll fold it down to a Reader to return a single value

alexlynham11:02:23

where the Reader is the product of either left or right of the ReaderEither

alexlynham11:02:54

so finally is just a function that lifts into the Reader context in the fold action and helps deliver that final type?

mccraigmccraig11:02:11

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)

alexlynham11:02:42

right, but a closure with finally in it will return a value

mccraigmccraig11:02:01

yes, but always the same value that was put into it, augmented with any effects that were output in the finally block

mccraigmccraig11:02:36

it's straightforward to implement, but i haven't figured out if and how it follows any of the associativity laws yet

alexlynham11:02:38

so it's like identity + side effects

alexlynham11:02:23

cos the side effects can only really work on the monadic context i guess

alexlynham11:02:28

need to ponder further

mccraigmccraig11:02:20

yeah, i'm in ponderland

alexlynham11:02:24

is there any reason the finally block couldn't accept a binding that operated on the wrapped type passed to the finally block?

alexlynham11:02:52

i know that maybe i'm a little obsessed with the idea it's just a fold and a lift

folcon11:02:36

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 😃...

mccraigmccraig12:02:04

oh, my bad @folcon - yesterday rather got away with me 😬

3
mccraigmccraig12:02:01

@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"}}

alexlynham12:02:55

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

mccraigmccraig12:02:40

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

alexlynham13:02:24

using monads or an effect type?

mccraigmccraig13:02:40

using a writer monad with an effect-accumulation monoid

mccraigmccraig13:02:22

basically just a map of {effect [effect-val]}

alexlynham13:02:24

so you can just reduce the effects at will, regardless of whether it's sync or async?

alexlynham13:02:55

looks suspiciously like a variant type 👀

mccraigmccraig13:02:31

no, the map can hold many effect descriptions

mccraigmccraig13:02:02

so {:push [{:token "abc" :message "hey"}] :websocket [{:channel "/users/123" :data {...}}]}

mccraigmccraig13:02:37

could equally be a list of variants though

alexlynham13:02:21

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

alexlynham13:02:00

...unless that is the intention here

mccraigmccraig13:02:39

i thought the map of lists was cute 😬... it's just a monoid interface impl though, it's easy to switch out

mccraigmccraig13:02:37

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

alexlynham13:02:58

we have two similar things in current codebase

alexlynham13:02:30

one is xfm data -> write data -> get db driver return -> xfm that into a serialised form

alexlynham13:02:03

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

alexlynham13:02:34

so we've ended up just abstracting and abstracting, but that has relied on a disciplined effort

alexlynham13:02:14

it would be easy for a future dev team to completely trip over it if they weren't disciplined, it would get verbose fast

alexlynham13:02:01

transformation

alexlynham13:02:50

think i might even have pinched that shorthand from hickey

mccraigmccraig13:02:15

that was my first guess 🙂 then i realised i wasn't sure

alexlynham13:02:39

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'
  ...
}

alexlynham13:02:57

spring can't come soon enough

folcon21:02:10

Oh, that doesn't just work? :(....

mccraigmccraig07:02:36

it just works for web pages where the sourcemap is hosted alongside the javascript... but that can't work for mobile apps