funcool

A channel for discussing and asking questions about Funcool libraries https://github.com/funcool/
jaen 2016-01-29T18:46:13.000078Z

@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

jaen 2016-01-29T18:46:44.000080Z

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

jaen 2016-01-29T18:46:52.000081Z

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

niwinz 2016-01-29T18:47:13.000082Z

This is because you are using the validation type

niwinz 2016-01-29T18:47:23.000083Z

validation type expects map like objects

jaen 2016-01-29T18:48:10.000084Z

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

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

jaen 2016-01-29T18:48:20.000085Z

Unless I'm entirely misunderstanding how to use it.

niwinz 2016-01-29T18:51:23.000086Z

hmm,

niwinz 2016-01-29T18:51:26.000087Z

you are right

jaen 2016-01-29T18:55:15.000088Z

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.

jaen 2016-01-29T18:55:24.000089Z

But I can't seem to achieve that properly.

niwinz 2016-01-29T18:55:30.000090Z

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

niwinz 2016-01-29T18:56:52.000092Z

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

jaen 2016-01-29T18:57:36.000093Z

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

niwinz 2016-01-29T18:57:45.000094Z

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

jaen 2016-01-29T18:57:46.000095Z

(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}>

niwinz 2016-01-29T18:58:07.000096Z

if you see in my last example

niwinz 2016-01-29T18:58:27.000097Z

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

niwinz 2016-01-29T18:59:12.000098Z

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

jaen 2016-01-29T18:59:12.000099Z

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

jaen 2016-01-29T18:59:35.000100Z

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

jaen 2016-01-29T19:00:04.000101Z

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.

niwinz 2016-01-29T19:00:25.000102Z

effectivelly 😉

jaen 2016-01-29T19:01:10.000103Z

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

niwinz 2016-01-29T19:01:26.000104Z

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

niwinz 2016-01-29T19:01:34.000105Z

let see some more functions in the sugar ns

niwinz 2016-01-29T19:01:47.000106Z

there are a lot of useful stuff for work with applicatives

niwinz 2016-01-29T19:01:58.000107Z

they surelly in future will be available on core ns.

jaen 2016-01-29T19:06:56.000109Z

Gotcha; I'll use ap then. Thanks!

jaen 2016-01-29T21:27:58.000110Z

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

niwinz 2016-01-29T21:41:16.000111Z

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

jaen 2016-01-29T21:43:09.000112Z

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

jaen 2016-01-29T21:47:44.000113Z

Hm, one thing I noticed

jaen 2016-01-29T21:47:54.000114Z

You're using the same context in both examples

jaen 2016-01-29T21:47:58.000115Z

I'm using a different context.

jaen 2016-01-29T21:48:02.000116Z

Maybe that's the cause

jaen 2016-01-29T21:48:04.000117Z

?

jaen 2016-01-29T21:48:12.000118Z

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

jaen 2016-01-29T21:48:14.000119Z

Doesn't seem to work

jaen 2016-01-29T21:48:22.000120Z

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

niwinz 2016-01-29T21:50:25.000121Z

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

niwinz 2016-01-29T21:50:54.000122Z

and we are working on make it in different manner

niwinz 2016-01-29T21:51:14.000123Z

but it still not ready 😞

jaen 2016-01-29T21:52:01.000124Z

Hah, thanks

jaen 2016-01-29T21:52:04.000125Z

(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]})

jaen 2016-01-29T21:52:05.000126Z

works

jaen 2016-01-29T21:52:24.000127Z

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

jaen 2016-01-29T21:52:25.000128Z

Good to know

niwinz 2016-01-29T21:54:24.000129Z

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

jaen 2016-01-29T21:55:16.000130Z

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

niwinz 2016-01-29T21:55:52.000131Z

that are the tradeoffs of dynamic vs static

jaen 2016-01-29T21:56:23.000132Z

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

jaen 2016-01-29T21:56:44.000133Z

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

niwinz 2016-01-29T22:13:55.000134Z

Yes, this is the plan

niwinz 2016-01-29T22:14:03.000135Z

You are welcome!

niwinz 2016-01-29T22:14:14.000136Z

The plan is implement them different

niwinz 2016-01-29T22:14:53.000137Z

That will be the cats 2.x.. branch

niwinz 2016-01-29T22:15:19.000138Z

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

niwinz 2016-01-29T22:15:35.000139Z

the both major branches will be maintained for some time