[plt-scheme] Why do layman programmers care about Currying?
I think you've misunderstood monads: see my comments below.
On Jan 1, 2009, at 2:31 PM, Joe Marshall wrote:
> On Wed, Dec 31, 2008 at 5:55 PM, Gregory Woodhouse
> <gregory.woodhouse at gmail.com> wrote:
>> Isn't the point of using monads....
>
> I'm not a real fan of monads, but the original idea was to take a
> program that
> operated on base types, like int -> int, and transform it to a
> program that
> operated on (f int) -> (f int). In this new space, you can plug the
> elements
> together to create a program that operates on ints, but is
> explicitly `wired'
> to behave in a certain way.
>
> For example, you can mimic state by passing around a state variable.
> You tranform your int -> int functions to (f int state) -> (f int
> state), plug the
> transformed functions together linearly so the state variable behaves
> correctly, then run the resulting program.
>
> I'm sure you've done something similar with Scheme programs by
> passing around an accumulator or something.
>
> Now here's the magic part. If you look at the transformed code,
> you'll
> see that the bulk of it isn't really using the state variable, it is
> simply
> passing it along to the next guy. So move the state variable to the
> end
> of the argument list, *curry the function*, and voila! the state
> variable
> is gone! You can do this to most of the code and make the state
> variable virtually disappear.
>
> What you've done is factored your code such that the `plumbing'
> --- the state variable and associated argument passing --- can be
> separated from the more interesting computation.
Yep.
> The step of moving the state variable to the end of the argument list
> and currying is critical. The trick wouldn't be nearly as interesting
> if you couldn't drop the curried variable out of the picture. In
> Scheme,
> this is a bit of a pain because currying the argument list means
> changing *every* call site. In Haskell, however, you can simply
> omit the required variable and the language takes care of it.
What you're saying makes some sense, but I don't think you'd describe
this programming pattern as being a monadic one.
As a matter of fact, using e.g. the store monad in Haskell *does*
require quite an elaborate rewrite, typically using 'do' & 'return' in
every function to be transformed. The 'do' monad can easily be
implemented as a scheme macro (see below). Using this macro in Scheme
is exactly as much work as using 'do' in Haskell.
Returning to your proposed programming pattern: yes, what you're
describing could be useful in implementing store-passing, but (unlike
'do') it doesn't help with the much more painful part of store-
passing, which is threading the store through multiple calls made by a
single procedure.
There *is* one way in which Haskell's 'do' is much nicer than this
one: Haskell's type system makes it possible to define a 'do' that
works for a whole bunch of different monads, and isn't chained to just
one. In Scheme, if you want to use 'do' for different monads, you'll
probably have to enrich uses of 'do' to indicate which monad's 'bind'
to expand into.
> That's why I said that the Haskell community would be far less
> excited about monads if their language didn't automagically do the
> currying.
By the above, I believe this is incorrect. I would instead suggest
that the Haskell community would be far less excited about monads if
their language provided mutation.
Feel free to correct any mistakes I've made,
John
Implementation of 'do' for the state monad in plt scheme. This expands
into uses of 'bind', so you'd better have 'bind' defined in this
scope. There's also an example, which depends the particulars of the
homework assignment it was a part of (including PLAI-style
structures), but should serve to illustrate (assuming you're already
fairly familiar with Haskell's 'do').
;; sdo : an imitation of Haskell's do:
(define-syntax (sdo stx)
(letrec
([popclause (lambda (clause)
(syntax-case clause (<-)
[(a <- rhs) (list #'a #'rhs)]
[rhs (list #'bogus #'rhs)]))]
[rearrange (lambda (lopops)
(if (null? (cdr lopops))
(cadar lopops)
#`(bind #,(cadar lopops) (lambda (#,(caar
lopops)) #,(rearrange (cdr lopops))))))])
(syntax-case stx ()
[(_ clause ...)
(let* ([popped (map popclause (syntax->list #'(clause ...)))])
(rearrange popped))])))
Here's an example of using it:
(test
(run
(sdo (s <- getstore)
(p <- (return (litV (+ (litV-val (store-lookup s 0)) 1))))
(setstore (aSto 0 p s))
(return 4))
(aSto 0 (litV 4) (mtSto)))
(a*s 4 (aSto 0 (litV 5) (aSto 0 (litV 4) (mtSto)))))
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 2484 bytes
Desc: not available
URL: <http://lists.racket-lang.org/users/archive/attachments/20090103/ac400c77/attachment.p7s>