Fork me on GitHub
#clojure-uk
<
2017-09-29
>
otfrom07:09:21

@mccraigmccraig clojure API nirvana?

otfrom07:09:27

this I want to hear about

thomas07:09:45

moin moin morning

thomas07:09:39

@otfrom Clojure Nirvana probably smells like teen spirit

mccraigmccraig07:09:36

@otfrom it's nothing magical - just a combination of a few things which have suddenly clarified the code - reducing yada handlers to almost nothing apart from the HTTP-related declarations, and then easy automatic error-handling and response-encoding through the happy medium of short-circuit promise-chain evaluation

peterwestmacott07:09:12

“reducing yada handlers to almost nothing” ???

otfrom07:09:43

sounds like there was a bit of an epiphany tho

mccraigmccraig08:09:59

@peterwestmacott e.g. https://gist.github.com/mccraigmccraig/30912c19c88c3c9f297da2574e3d3faf - the yada handler function has become standard and i almost don't have to think about the HTTP transport anymore - parameter decoding & coercion is all automatic and result and error capture and encoding is also automatic

mccraigmccraig08:09:44

nothing magical - but suddenly all the pieces fitted together and a lot of needless complexity disappeared

mccraigmccraig08:09:08

although, thinking about it, short-circuit promise-chain evaluation is fairly magical 🙂

reborg09:09:00

@mccraigmccraig I'm going to try out the yada way soon, but having used metosin/compojure-api any idea how they differ/compare? I think metosin is handling the auto-coercion with muuntaja (yay, another name)

mccraigmccraig09:09:48

i've not tried metosin/compojure-api @reborg - it looks like v2.0 might be very similar to yada+bidi from the readme. if you are comfortable (or want to get comfortable) with monads i would highly recommend cats+manifold for composing ops for async apis

reborg10:09:03

The M word 🙂 I usually stay away from that. But will use yada in anger soon, so will report back

mccraigmccraig10:09:59

given that i seem to be #clojure-uk's resident M-evangelist i should try and convince you why they are great, even in clojure, and especially for async 😬

sundarj10:09:27

monads ftw

mccraigmccraig10:09:51

this is an api handler from our app, behind a yada resource - https://gist.github.com/mccraigmccraig/86c4e60eae1462f2c37a738268ca0c4c

mccraigmccraig10:09:03

every step in the ddo (some sugar around cats mlet) bindings block which is outside of a :let is an async operation returning a promise of something

mccraigmccraig10:09:52

the monad unwraps the values from the promise and binds them, and ensures that any errors in the steps shortcut the evaluation

mccraigmccraig10:09:43

but the code reads very closely to synchronous code - much nicer than callback chains or manifold's chain fn approach

mccraigmccraig10:09:30

and there is nothing magical at all - just some (very) well thought out composition of lexical closures

reborg10:09:53

core.async would also read linear, why are u using manifold?

mccraigmccraig10:09:43

i started with manifold because yada was using it - but it turns out i prefer promises to channels for non-stream-based things - the error-handling and composition story is better

mccraigmccraig10:09:51

although, since i'm using a promise-monad, i could switch to using core.async as the underlying async provider without changing (barely) any code at all - a core.async promise monad is straightforward to define - https://github.com/funcool/cats/blob/master/src/cats/labs/channel.cljc

mccraigmccraig10:09:03

tho that code is kinda old and would probably benefit from using a promise-chan

mccraigmccraig10:09:57

i actually did this on the client-side a while back (switched from using core.async as my async provider to using js/bluebird promises) and it was very straightforward

reborg10:09:04

Well, guess it's working good for you!

reborg10:09:32

Any chance you can present your finding at some conf?

mccraigmccraig10:09:29

i was thinking about that - there's a bunch more stuff i want to do - in particular i'd like to create a composite monad, especially with something like a writer added in to permit detailed async tracing (to counter the loss of stacktraces), and maybe a reader to avoid having to pass app-contexts around as fn args... so monad-values become Promise<Reader,Writer,step-value>

mccraigmccraig10:09:28

that would be very cool, and i could do a presentation around that

mccraigmccraig10:09:52

but not for a couple of months yet - i have a lot of customer-requests to satisfy first 🙂

otfrom11:09:25

pfff.... customers... 😉

peterwestmacott13:09:26

“Also, there is no upsell on message history. It’s free to get to your old messages.” - quote from Keybase “Teams” announcement.

dominicm13:09:58

yeah, I think there's a lot of resentment for how slack are presenting things currently.

dominicm13:09:04

I reserved the juxt team already

guy13:09:32

resentment for a free service?

dominicm13:09:55

@guy not for all of slack, just the… approach to marketing that is being taken for their paid tiers. Especially given how "cool" they've been up until now.

maleghast17:09:59

Hello All… I was just wondering if there is an “obvious” way to sort a vector of maps based on two values in each map, i.e. sort by id, date rather than just id?

maleghast17:09:56

Clearly I can use sort-by to sort by id OR date, no problems, but I can’t see a way to sort by id AND date without sorting by id and then slicing up the vector and re-sorting each part by date…

mccraigmccraig17:09:07

@maleghast sort has a 2-arg version with a comparator which can be something like #(let [id-s (compare (:id %1) (:id %2))] (if (not= 0 id-s) id-s (compare (:date %1) (:date %1))))

mccraigmccraig17:09:32

(which i haven't tried out so caveat executor)

maleghast17:09:02

@mccraigmccraig Thanks, I will try that and let you know…

minimal17:09:22

(sort-by (juxt :date :id)

mccraigmccraig17:09:00

oh, yeah, that's much nicer

maleghast17:09:16

Yeah, that’s AWESOME!

maleghast17:09:32

@minimal - Thanks, that is really lovely :[-)

minimal17:09:57

it’s usually the only thing i can remember juxt is good for

minimal17:09:38

good with group-by as well

maleghast17:09:54

I can see that, yes

maleghast17:09:00

(now, in hindsight, clearly 😉 )

thomas17:09:56

with hindsight almost everything is obvious... even last weeks lottery numbers.

maleghast18:09:33

All I have to do, now, is take the sorted vector and split it into partitions based on the date field and the id field… So all the maps that are for the same id and date need to end up in their own nested vector…

maleghast18:09:42

would that work with group-by, I wonder…?

minimal18:09:57

sounds like a group-by

maleghast18:09:11

Yeah, this looks like it does the trick…

(group-by :station (sort-by (juxt :station :date) (:results example-data)))

maleghast18:09:55

But I think it might be better like this:

(sort-by :date (group-by :station (:results example-data)))

maleghast18:09:52

No, that doesn’t work, but I can group-by station and then do-seq over the grouped vector of maps and then group each one by date and the sort by date before I process them, and that will work very nicely… So I didn’t need the juxt on station and date, but it’s good to know how it works anyway… 🙂

maleghast18:09:38

I’ve just done some playing around with group-by and I find myself wondering where this function has been all my life….