@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
I suppose part of it is because Haskell has support for currying and Clojure doesn't
But I'm not sure how I should be using it then
This is because you are using the validation type
validation type expects map like objects
But with map it doesn't seem to work either:
(m/<*> (av/ok merge) (av/ok {:a 1}) (av/ok {:b 2}))
=> #<Ok nil>
Unless I'm entirely misunderstanding how to use it.
hmm,
you are right
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.
But I can't seem to achieve that properly.
user=> (require '[cats.labs.sugar :as s])
nil
user=> (s/ap + (maybe/just 1) (maybe/just 2))
#<Just 3>
The <*>
fails because clojure does not have auto curry
Ha, thanks this seems to be working as it should!
user=> (m/fapply (maybe/just (m/curry 2 +)) (maybe/just 1) (maybe/just 2))
#<Just 3>
(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}>
if you see in my last example
If I provide auto currying + function for two parameters, it works as expected
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
Yeah, I can see that, though in my case the number of validators is variable... but I suppose I just can count them.
But ap
seems to be more readable. Is that something that should not be used since it's in the labs
namespace?
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.
effectivelly 😉
Right. So is ap
okay to use or should I prefer m/curry
?
ap is ok to use, in fact is very very useful
let see some more functions in the sugar ns
there are a lot of useful stuff for work with applicatives
they surelly in future will be available on core ns.
Gotcha; I'll use ap
then. Thanks!
Hmm, it seems when I try to fmap
inside fmap
it doesn't work; is that expected?
As far as I can see, it works: (m/fmap (fn [items] (m/fmap inc items)) [[1 2 3] [1 2 3]])
Ok, then maybe I'm doing something wrong, let me make a better example.
Hm, one thing I noticed
You're using the same context in both examples
I'm using a different context.
Maybe that's the cause
?
(m/fmap (fn [elem]
(let [[k v] elem]
[k (m/fmap inc v)]))
{:a [1 2 3] :b [1 2 3]})
Doesn't seem to work
(unless I'm doing something obviously wrong that is)
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
and we are working on make it in different manner
but it still not ready 😞
Hah, thanks
(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]})
works
I didn't understand from your previous explanation that it's needed when you nest abstractions of different type.
Good to know
due to the nature of dynamic language is a little bit difficult infer the context
Yeah, not surprising at all. Types certainly help in such cases a lot, so you have to make do with what you can.
that are the tradeoffs of dynamic vs static
Yep; anyway - thanks a lot for the help : )
Do you plan to add a sort of stack for the contexts or implement it differently altogether?
Yes, this is the plan
You are welcome!
The plan is implement them different
That will be the cats 2.x.. branch
it there are plans for make as less breaking changes possible, but is inevitable I think
the both major branches will be maintained for some time