Git Product home page Git Product logo

Comments (28)

yurrriq avatar yurrriq commented on August 15, 2024

I'd like to work on this one.

from cats.

niwinz avatar niwinz commented on August 15, 2024

Fine! Github doesn't allows assign issues to external people. So we keep in mind that you are working on this. Thanks!

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

Cool. Working on it here.

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

Looks like I don't have the proper time to commit to this right now.

Here's what I came up with so far:

(defn foldseq
  [f val mvs]
  {:pre [(not-empty mvs)]}
  (let [ctx (get-current-context (first mvs))]
    (with-monad ctx
      (reduce (fn [mz mv]
                (mlet [z mz
                       v mv]
                  (f z v)))
              (return val)
              mvs))))

Might be obsoleted by #62.

from cats.

 avatar commented on August 15, 2024

Thanks for your time @yurrriq, looks like a generic foldlM and foldrM can be implemented in terms of Foldable's foldl and foldr.

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

I figured. Can't wait to play around with it!

from cats.

niwinz avatar niwinz commented on August 15, 2024

This is already implemented in 9e812ec

Thank you very much for the initial implementation.

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

👍

from cats.

niwinz avatar niwinz commented on August 15, 2024

I have reopened this issue because the foldM finally is not merged into master.
We should research on how to implement correctly the foldM.

Additional notes: http://members.chello.nl/hjgtuyl/tourdemonad.html#foldM

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

I've got another implementation almost ready. It takes a context as the first argument. Without static typing of the fold function, that might be the only way..

from cats.

 avatar commented on August 15, 2024

Yes, since the monadic type is what the reducing function returns. Looking forward to see what you've come up with @yurrriq!

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

Edit: This code is at yurrriq/cats@foldM.

Second Draft

It's basically a literal translation of the Haskell:

foldM             :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
foldM _ a []      =  return a
foldM f a (x:xs)  =  f a x >>= \fax -> foldM f fax xs
(defn foldm [ctx f z xs]
  (letfn [(foldm' [z' xs']
            (if (empty? xs')
              (return ctx z')
              (let [[h & t] xs']
                (>>= (f z' h) (fn [z''] (foldm' z'' t))))))]
    (foldm' z xs)))

Example

Based on the example at the link @niwinz posted.

(require '[cats.monad.maybe :as maybe])

(defn- m-div [x y]
  (if (zero? y)
    (maybe/nothing)
    (maybe/just (/ x y))))

(foldm maybe/maybe-monad m-div 1 [1 2 3])
;; => #<Just 1/6>

(foldm maybe/maybe-monad m-div 1 [1 2 0])
;; => #<Nothing>

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

Another approach would be to use custom metadata on the folding function to store the context of the return value and then pass it as a var, e.g.

(defn- m-div
  {:cats.core/context maybe/maybe-monad}
  [x y]
  (if (zero? y)
    (maybe/nothing)
    (maybe/just (/ x y))))

(defn foldm*
  [f z xs]
  {:pre [(->> (get (meta f) ::context) (satisfies? p/Monad))]}
  (let [ctx (get (meta f) ::context)]
    (letfn [(foldm' [z' xs']
              (if (empty? xs')
                (return ctx z')
                (let [[h & t] xs']
                  (>>= (f z' h) (fn [z''] (foldm' z'' t))))))]
      (foldm' z xs))))


(foldm* #'m-div 1 [1 2 3])
;; => #<Just 1/6>

A good example of a library that uses similar metadata management is dire.

But having to pass vars around is less than ideal. Is there a way to attach metadata to anonymous functions? We could perhaps use with-meta to attach :cats.core/context to anonymous functions.

Edit: It works!

(foldm*
 (with-meta
   (fn [x y]
     (if (zero? y)
       (maybe/nothing)
       (maybe/just (/ x y))))
   {:cats.core/context maybe/maybe-monad})
 1
 [6 5 4 3 2])
;; => #<Just 1/720>

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

It's pretty trivial to write a macro to wrap anonymous functions in a with-meta form.

(defmacro mfn [ctx & body]
  `(with-meta (fn ~@body) {:cats.core/context ~ctx}))

(foldm*
 (mfn maybe/maybe-monad
   [x y]
   (if (odd? y)
     (maybe/nothing)
     (maybe/just (* x y))))
 1
 (repeat 8 2))
;; => #<Just 256>

But what could be even better (or maybe both would be good) is to add a third arity to lift-m that wraps a call to the two argument lift-m in a with-meta form.

Sorry for lengthy thought dumps..

from cats.

 avatar commented on August 15, 2024

Sorry for lengthy thought dumps..

No problem, it's great food for thought! However I think the metadata approach can be moved to separate
conversation about context management, the current approach is not very convenient for things like monad
transformers and we may be able to improve it using metadata. Feel free to open an issue to start the
discussion!

More succinctly without the currying:

(defn foldm [ctx f z xs]
  (letfn [(foldm' [z' xs']
            (if (empty? xs')
              (return ctx z')
              (let [[h & t] xs']
                (>>= (f z' h) (fn [z''] (foldm' z'' t))))))]
    (foldm' z xs)))

Looks good! We added a currying macro recently that can be used together with foldm in case you want currying.

We may want do to the ctx argument optional, having 3 arity when we know xs is not empty and >>= will
derive and set it. This way, the call to return in foldm' can be left as (return z').

(defn foldm
  ([f z xs]
    (letfn [(foldm' [z' xs']
              (if (empty? xs')
                (return z')
                (let [[h & t] xs']
                  (>>= (f z' h) (fn [z''] (foldm' z'' t))))))]
      (foldm' z xs)))
  ([ctx f z xs]
    ;; ...
    ))

In cases when xs is not empty the context will be set by the first >>= and in places like mlet the
context is already set.

(require '[cats.monad.maybe :as maybe])

(defn- m-div [x y]
  (if (zero? y)
    (maybe/nothing)
    (maybe/just (/ x y))))

(foldm m-div 1 [1 2 3])
;; => #<Just 1/6>

;; we may want to provide a friendlier error message here
(foldm m-div 1 [])
;; IllegalArgumentException You are using return/pure/mzero function without context.  cats.context/get-current (context.cljc:62

(foldm maybe/maybe-monad m-div 1 [])
;; => #<Just 1>

(mlet [x (maybe/just 1)
       y (foldm m-div 1 [1 2 3])]
  (return (+ x y)))
;; => #<Just 7/6>

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

I love that!

With the ternary implementation I just added to my branch:

(let [f m-div, z 1, xs [1 2 3]]
  (= (foldm maybe/maybe-monad f z xs)
     (foldm f z xs)))
;; => true

(foldm m-div 1 [])
;; => AssertionError Assert failed: (seq xs)  cats.core/foldm

(foldm maybe/maybe-monad m-div 1 [])
;; => #<Just 1>

from cats.

 avatar commented on August 15, 2024

Looks awesome.

Maybe the precondition is a bit too strict. For example I may be using foldm wrapped in a with-context macro that sets the context and expect that if given an empty sequence foldm wi'll be able to use return because context is set.

(with-context maybe-monad
  (foldm m-div 1 []))
;; => #<Just 1>

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

Would something like (or *context* (seq xs)) (or (seq xs) (get-current-context)) work?

from cats.

 avatar commented on August 15, 2024

I think so. Note that *context* now lives in the cats.context namespace.

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

Noted. It doesn't quite work. I'm experimenting now.

from cats.

yurrriq avatar yurrriq commented on August 15, 2024
(defn foldm
  ([f z xs]
   (if (empty? xs)
     (return (get-current-context) z)
     (let [[h & t] xs]
       (mlet [z' (f z h)]
         (if (empty? t)
           (return z')
           (foldm f z' t))))))
  ([ctx f z xs]
   (if (empty? xs)
     (return ctx z)
     (foldm f z xs))))

(with-context maybe-monad
  (foldm m-div 1 []))
;; => #<Just 1>

(foldm m-div 1 [])
;; IllegalArgumentException You are using return/pure/mzero function without context.  cats.context/get-current (context.cljc:62

(foldm maybe-monad m-div 1 [])
;; => #<Just 1>

A perhaps undesirable side effect, however, is this:

(with-context either-monad
  (foldm m-div 1 [1 2 3]))
;; => #<Just 1>

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

Maybe a precondition is not the best choice here anymore, since this latest idea ends up calling (seq xs) and possibly (get-current-context) twice.

from cats.

 avatar commented on August 15, 2024

Maybe a precondition is not the best choice here anymore, since this latest idea ends up calling (seq xs) and possibly (get-current-context) twice.

Let's get rid of it then, no problem.

@niwinz what do you think about the current proposal?

from cats.

niwinz avatar niwinz commented on August 15, 2024

As quick review seems ok for me!

@yurrriq feel free open the pr for the final review and merge!

And thank you very much to both!

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

👍 I'm AFK for the next couple days but I should be able to clean up, write tests and make a PR on Sunday (CDT).

from cats.

 avatar commented on August 15, 2024

No hurries, thanks for your time!

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

Sounds good. PS I'm hoping to port cats to LFE in the reasonably near future, using flavors.

from cats.

yurrriq avatar yurrriq commented on August 15, 2024

I think this can be closed now. 😄

from cats.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.