@mccraigmccraig this is what I got:
(->> (exc/success "<https://www.sapo.pt>")
(cats/fmap parse-url)
(cats/fmap open-connection)
(cats/fmap to-stream)
(cats/fmap slurp))
a bit better I should say
you could write a new thread macro which repeatedly fmap
s, along the lines of ->>=
... if there isn't one already... i haven't looked
had a look, can't see one
and what about flatmap? I copy-pasted bind and did a recursive call but I was wondering if this is a need
(defn flatmap
"Stack-unsafe, flatmapping version of `cats.core/bind`.
Use at your own peril."
[mv f]
(let [ctx (cats.context/infer mv)]
(cats.protocols/-mbind ctx mv
(fn [v]
(cats.context/with-context ctx
(if (satisfies? cats.protocols/Contextual v)
(flatmap v f)
(f v)))))))
it should be able to do a safer version with some kind of fold or loop/recur I guess
may I ask how/if are you using cats? some complaints I read is that the monads end up infecting all functions
sure - by far our biggest use is to make async programming sane, so we heavily use the manifold deferred (clj) and promesa (cljs) promise monads
we also use a custom monad for hiccup markup
and our application context builder https://github.com/employeerepublic/deferst uses a deferred-state monad transformer
i haven't found monads infect everything - but that is perhaps because we mostly use monads over otherwise sane datastructures for our purpose
(rather than constructing new monadic types)
e.g. all of our promise monad stuff uses regular promises (deferred on clj or bluebird on js) so there's no odd infecetion
so I’m guessing manifold’s let-flow
isn’t enough for your use case right? you probably mix and match deferreds with other monads - is this the case?
I’ve used manifold before but I’ve never used monads in clj
let-flow
i think has power equivalent to cats alet
- we use alet
occasionally but mostly we use mlet
which introduces stepwise operation
but i wanted to use the same idioms for async programming on both the front-end and back-end, and let-flow
is clj only
so cats permits more programming consistency across our codebase
cool, didn’t know let-flow was backend only
i have heard of projects which create their own monad types having problems with infection - i think @jarohen had one - maybe yoyo ?
and it does seem that there are probably lots of places where monads make sense in other langs (with static checking) where they don't make sense in clojure
but when you want something to give you some well known and tested ways of dealing with a sensible datatype then i've found them to work well in clojure
ooo i just noticed the state monad got re-added to cats
hmm i missed it, gonna take a look!
thanks for your comments
we’re gonna migrate a rails app to clj soon and we’re evaluating stuff
out of interest (and if you are at liberty to answer 😬) what persistence and lib choices are you leaning towards ?
well, I guess we’re keeping our postgres db, so hikari cp, and either honeysql/yesql for db access
compojure-api and mount
we’re missing a validation story besides the coercion aspect of compojure-api, thats why I was looking into cats
how would cats help with validation ?
we use yada which has schema-based coercion/validation (probably similar to compojure-api)... and we also use schema/defn in selected other places throughout the codebase for further validation
well if I determine a request is invalid, I can build a pipeline of ops knowing that it will short-circuit
ah, yeah, short-circuiting we use heavily
it's invaluable
so mostly for that
I see that scala/cats has a validated which is an applicative functor, I guess a good exercise would be do something like that for clj/cats
otherwise, either
won’t accumulate errors
oh, does that permit additional context to be added to errors by callers ?
it seems so but I’m not familiar with it
I’m just googling for stuff
oh, i see... not what i thought, but looks like it could be handy
in the end, when validation runs it seems you get an either
since we're all async we tend to use the error-condition of promises rather than either - which can (handily) usually be set just by throwing an exception