[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Chicken-users] [ANN] Monad Egg

From: Daniel Leslie
Subject: Re: [Chicken-users] [ANN] Monad Egg
Date: Sun, 15 Apr 2012 08:55:01 -0700


I'm not an expert, but I'll try to explain how I see things and maybe it will make sense.

Perhaps consider that the <complex-id> structure created with define-monad is not the monad itself, but the set of operators and some helpful handlers for dealing with the monad.

Consider the signature for the bind function:

M a -> (a -> M b) -> M b

We have some monad M a and a function that takes a value and produces another monad of the same type. It's the bind function's job to "execute" M a in order to retrieve the value to pass into the function. The function returns a monad of the same type, which the bind function can now manipulate.

For instance, a bind function for a state monad would pass the storage container into M a and receive the original a and the storage container s. We then pass the value a into the provided function, which returns a monad M b of the same type. Here's where it's tricky: M b has yet to be evaluated and has not modified the storage container. So, the bind function passes the storage container s to the M b it received and returns the outcome, which happens to have the same type M b.

1. Evaluate M a with state s to get a
2. Evaluate (a -> M b) with a to get M b
3. Evaluate M b with state s in order to construct (renamed for the sake of clarity) M b' with the unit function or equivalent
4. Pass M b' forward

Ok, so why M a -> (a -> M b) -> Mb and not (a -> M b) -> M a -> Mb?

I'm free-handing this, so consider it pseudo-code:

Here's how it works now:

(using <id>
  (>>= (>>= (return 1)
              (lambda (x) (+ x 1))))
          (lambda (y) (+ y 2))))


(using <id>
  (>>= (lambda (y) (+ y 2))
    (>>= (lambda (x) (+ x 1))
              (return 1))))

I think I got that right... Basically, the order of operations becomes inverted when you begin combining binding functions. You end up with the logically last function to be applied appearing at the top of the binding chain.

Other than that, I imagine it's typically done this way because putting the first Monad to be executed at the start of the signature makes it easier to curry the function? I dunno, that's purely conjecture on my part.

I may have derped heavily in this reply, so please forgive me if I made an egregious error. I haven't had coffee yet. :)


On Sun, Apr 15, 2012 at 07:08, Jörg F. Wittenberger <address@hidden> wrote:
On Apr 13 2012, Daniel Leslie wrote:

 (lambda (r i) (values r i))
 (lambda (f r i) (f r i)))

Why not:

(define-monad <cid> (lambda (a) (values (car a) (cdr a)))
  (lambda (a f) (f (car a) (cdr a))))

That's how the <logger> monad example goes about things. The parameter list
itself is just compacted into a pair, and passed through the unit and bind

I suppose I could extend it to support a full parameter list... It's
certainly worth considering.

I'm pretty confused now.

A) About the parameter order.
B) the approach to multiple values.

Let's start with (B) for logic even though unimportant

(define-monad <cid> (lambda (a) (values (car a) (cdr a)))
  (lambda (a f) (f (car a) (cdr a))))

Confused I am because where the identity returns one
value it return now two.  Access however is as if it
had returned a list.

As for (A): The API-point of mine derives from the consideration,
what changes would be needed to support a full parameter list.
This lead me to see the "f" parameter as naturally in the first
position.  Not

(define-monad <id> (lambda (a) a)
  (lambda (a f) (f a)))


(define-monad <id> (lambda (a) a)
  (lambda (f a) (f a)))

I would prefer.  I feel it looks more natural when extended to

(define-monad <cid> (lambda (a b) (values a b))
  (lambda (f a b) (f a b)))

because the "f" is always in "operator position".


reply via email to

[Prev in Thread] Current Thread [Next in Thread]