> > > Isn't that a bit of a dodgy argument?  I don't know of any papers
> > > on `in' polymorphism, but many about `let' polymorphism.  If I see
> > > `let', I expect polymorphism, and I'm not going to go searching for
> > > an `in'.
> > Not true (or if true, misguided).
> I say dodgy - you say misguided.  OK - a fair enough trade ;-)

Milner introduced polymorphic bindings via *non-recursive* `let's, 
suggesting a separate `let rec' as syntactic sugar for a combination
of `let' and `fix'. In the context of Haskell, `let' is really a `letrec'. 
I guess the syntactic distinction was droppped because the additional 
analysis and implicit grouping of recursive bindings allows one 
construct to serve both purposes.

Do you want to start a poll on how many Haskell users share the
"misguided" intuition about `let' for historic or other reasons? 
Count me in, then. If I see `let', I expect abstraction, and I'm not 
prepared to restrict something as essential as abstraction to 
monomorphic things just because `let' is used for two conflicting 
purposes.

Outside recursive `do's, this double usage just about works as
expected (most of the time), and if it doesn't inside, then one might
ask why `let' needs to be used at all.


To avoid confusion, I'll distinguish between the current,
non-recursive `do' and your new, recursive `dorec'.

`do' implies a monad, so we can always introduce non-recursive,
monomorphic bindings as

do
    a <- return e
    b <- return (f a)

Apart from aesthetic considerations, there are two reasons to add a
variant of `let' inside `do': recursive binding groups and polymorphic
bindings. Unfortunately, these two don't go together easily.

In your `dorec', we can already introduce recursive bindings

dorec
    a <- return (f b)
    b <- return (g a)

So the only reason for having a `let' inside `dorec' would have to
be polymorphism, right?

Marcin has made this point already, but I haven't seen any response
(apart from his own;-).


But then you say that you could handle polymorphic bindings with
a more complicated semantics. So I can see no reason at all for 
having `let' inside `dorec' - the only question is whether or not you 
want to support polymorphism as good as you can.

My view: if you can support polymorphism, do it!

The complicated semantics should default to the simpler one if no
polymorphism is needed, and if you don't want to go for the more
complete semantics, be open about it and let the implementation
issue an error message for cases it can't handle: "Sorry, not yet
implemented". I don't think you'll have to wait long for the error
reports coming in about this unimplemented feature.

Alternatively, keep `do' `(with `let'-polymorphism) and `dorec'
(with recursion) separate, and wait for the users to choose
between them..


Nobody want a messy semantics, but none of the two alternatives
sketched above looks good to me. If, instead, you would go for
a full implementation, you could take over the `do', drop `let' in
`do' and get a much more consistent picture:

`let' - potentially recursive and polymorphic bindings(*)
         in the identity monad, merging `let' and `letrec'
         (use of `=' indicates that lhs and rhs are on equal footing)

`do' - potentially recursive and polymorphic bindings(*)
         in some monad, merging `do' and `dorec'
         (use of `<-' indicates difference between monadic rhs 
          and plain lhs; monad needs to support fix if bindings are
          recursive; order of bindings drives translation)

(and then the ML folks will come and say that we should merge
`let' and `do' and keep recursion separate instead;-) 

I like your idea (if my interpretation of it is correct), but if you
describe such a proposal as `modest', I think you are only trying
to deceive yourself. It *is* a non-trival step, and if you are going 
to take it, please don't stop halfway through. The poll is rather
dubious as a basis for such a decision, and if anything, your 
extensions will increase the potential use of polymorphic
bindings in `do(rec)'s.

Claus

(*) as good as we know how to support. As Marcin pointed out,
    this might include permitting type declarations for bindings in
    `dorec', for the same reasons they are sometimes needed to
    get more polymorphism in `let(rec)'.

PS. I haven't looked into your papers yet, but `dorec' seems to
    require or emulate a more symmetric `bind' (>>=) than the 
    standard one (which has a continuation only in its second 
    parameter, and is thus biased towards left-to-right sequencing). 
    Does this suggest an overlap between `dorec' and arrows?



Reply via email to