Fork me on GitHub
#funcool
<
2016-01-29
>
jaen18:01:13

@niwinz: I have some problem understanding how to work with applicatives; the fail case works intuitively, but the ok case does not. In Haskell I can do

Prelude> (Just (+)) <*> (Just 1) <*> (Just 3)
Just 4
but in Clojure the same results in an error:
(m/<*> (av/ok +) (av/ok 1) (av/ok 2))
java.lang.ClassCastException: java.lang.Long cannot be cast to clojure.lang.IFn

jaen18:01:44

I suppose part of it is because Haskell has support for currying and Clojure doesn't

jaen18:01:52

But I'm not sure how I should be using it then

niwinz18:01:13

This is because you are using the validation type

niwinz18:01:23

validation type expects map like objects

jaen18:01:10

But with map it doesn't seem to work either:

(m/<*> (av/ok merge) (av/ok {:a 1}) (av/ok {:b 2}))
=> #<Ok nil>

jaen18:01:20

Unless I'm entirely misunderstanding how to use it.

niwinz18:01:26

you are right

jaen18:01:15

I tried a lot of combinations and none seemed to what I expected. I basically want to take my (av/ok {:some {:coerced "value"}}) and my (av/fail {:other {:value [:error/something]]}}) and reduce them to a one av/ok containing the validated map or to av/fail containing validation errors.

jaen18:01:24

But I can't seem to achieve that properly.

niwinz18:01:30

user=> (require '[cats.labs.sugar :as s])
nil
user=> (s/ap + (maybe/just 1) (maybe/just 2))
#<Just 3>

niwinz18:01:52

The <*> fails because clojure does not have auto curry

jaen18:01:36

Ha, thanks this seems to be working as it should!

niwinz18:01:45

user=> (m/fapply (maybe/just (m/curry 2 +)) (maybe/just 1) (maybe/just 2))
#<Just 3>

jaen18:01:46

(s/ap merge (av/ok {:a 1})
            (av/ok {:b 2})
            (av/fail (->ErrorContainer {:c [:fail]}))
            (av/fail (->ErrorContainer {:c [:fail-two]})))
=> #<Fail #validators.core.ErrorContainer{:v {:c [:fail :fail-two]}}>
(s/ap merge (av/ok {:a 1})
            (av/ok {:b 2}))
=> #<Ok {:a 1, :b 2}>

niwinz18:01:07

if you see in my last example

niwinz18:01:27

If I provide auto currying + function for two parameters, it works as expected

niwinz18:01:12

it is not very intuitive, I know, but clojure is no haskell and some of the haskell abstractions does not works in the same way in clojure

jaen18:01:12

Yeah, I can see that, though in my case the number of validators is variable... but I suppose I just can count them.

jaen18:01:35

But ap seems to be more readable. Is that something that should not be used since it's in the labs namespace?

jaen19:01:04

Yeah, I can understand why it would not work - you can't curry if you have varags as you wouldn't know when to stop.

niwinz19:01:25

effectivelly 😉

jaen19:01:10

Right. So is ap okay to use or should I prefer m/curry?

niwinz19:01:26

ap is ok to use, in fact is very very useful

niwinz19:01:34

let see some more functions in the sugar ns

niwinz19:01:47

there are a lot of useful stuff for work with applicatives

niwinz19:01:58

they surelly in future will be available on core ns.

jaen19:01:56

Gotcha; I'll use ap then. Thanks!

jaen21:01:58

Hmm, it seems when I try to fmap inside fmap it doesn't work; is that expected?

niwinz21:01:16

As far as I can see, it works: (m/fmap (fn [items] (m/fmap inc items)) [[1 2 3] [1 2 3]])

jaen21:01:09

Ok, then maybe I'm doing something wrong, let me make a better example.

jaen21:01:44

Hm, one thing I noticed

jaen21:01:54

You're using the same context in both examples

jaen21:01:58

I'm using a different context.

jaen21:01:02

Maybe that's the cause

jaen21:01:12

(m/fmap (fn [elem] 
          (let [[k v] elem]
            [k (m/fmap inc v)]))
  {:a [1 2 3] :b [1 2 3]})

jaen21:01:14

Doesn't seem to work

jaen21:01:22

(unless I'm doing something obviously wrong that is)

niwinz21:01:25

Yes, in this case you should specify the new context using with-context. This is something that I have explained the other day. The current approach for type/monad/context inference is a little bit limiting

niwinz21:01:54

and we are working on make it in different manner

niwinz21:01:14

but it still not ready 😞

jaen21:01:01

Hah, thanks

jaen21:01:04

(m/fmap (fn [elem] 
          (let [[k v] elem]
            (ctx/with-context (mp/-get-context v)
              [k (m/fmap inc v)])))
  {:a [1 2 3] :b [1 2 3]})

jaen21:01:24

I didn't understand from your previous explanation that it's needed when you nest abstractions of different type.

jaen21:01:25

Good to know

niwinz21:01:24

due to the nature of dynamic language is a little bit difficult infer the context

jaen21:01:16

Yeah, not surprising at all. Types certainly help in such cases a lot, so you have to make do with what you can.

niwinz21:01:52

that are the tradeoffs of dynamic vs static

jaen21:01:23

Yep; anyway - thanks a lot for the help : )

jaen21:01:44

Do you plan to add a sort of stack for the contexts or implement it differently altogether?

niwinz22:01:55

Yes, this is the plan

niwinz22:01:03

You are welcome!

niwinz22:01:14

The plan is implement them different

niwinz22:01:53

That will be the cats 2.x.. branch

niwinz22:01:19

it there are plans for make as less breaking changes possible, but is inevitable I think

niwinz22:01:35

the both major branches will be maintained for some time