RFC: Syntax for implicit parameter bindings
Manuel M. T. Chakravarty
chak@cse.unsw.edu.au
Mon, 04 Feb 2002 10:34:36 +1100
Dear Haskell Folks,
I am sure you are fully aware that the most painful
decisions that a language designer has to make are those
concerning the syntax of the language. Earth shattering
disputes have been fought over the syntax of programming
languages with equals only in arguments over the
monomorphism restriction...and, well, maybe also n+k
patterns.
In a previously closed discussion concerning the syntax of
implicit parameter bindings, it has been suggested to lay
the facts open to your scrutinising eyes, in the hope that a
wise decision will emerge.
The Status Quo
~~~~~~~~~~~~~~
Given a function that requires an implicit parameter
addBase :: (?base :: Int) Int => Int
addBase x = x + ?base
both GHC and Hugs currently support implicit parameter
bindings using syntax based on the keyword `with', as in
addBase 5 with ?base = 10
For the motivation behind implicit parameters and their
semantics, please see Lewis et al's POPL paper
<http://www.cse.ogi.edu/~jlewis/implicit.ps.gz>.
The Problem
~~~~~~~~~~~
Both GHC and Hugs support implicit parameters only when a
command line option is given that instructs them to enable
language extensions that go beyond Haskell 98. However, as
soon as this command line option, which activates all sorts
of extensions, is given, `with' suddenly becomes a keyword
and any Haskell code that uses `with' as a variable name
fails to parse. Given that `with' is a quite short and
rather common word, this problem is hard to neglect. In
particular, the XML library HaXML and the libraries in the
FFI Addendum use `with' for function names, which would be
awkward to change.
Now, given that implicit parameters are a - I believe -
rarely used extension, can't we just change the keyword to
something else and be done with it? We could, but implicit
parameters are an eminently useful feature that may well be
formalised into a Haskell 98 addendum, or even be included
in a future revision of the language. So, it seems
worthwhile to spent some thought on how to solve the problem
properly.
In addition to the keyword problem, `with' doesn't fit very
well in with other Haskell binding constructs, which are in
prefix form and extend as far to the right as possible (eg,
`let', `case', and lambdas). This, for example, raises the
questions what
let x = e1 in e2 with ?y = e3
might mean. (The exception to prefix form is `where', but
it scopes over groups of right-hand sides, not expressions.)
Possible Solutions
~~~~~~~~~~~~~~~~~~
[Ugly & easy]
The `with' keyword could be renamed to `iwith' or a prefix
form could be called `ilet' or `dlet' (the last of which
is actually accepted by Hugs as an alternative syntax).
[Just use let]
Alternatively, it seems reasonable to reuse the existing
`let' syntax. So, our above example would read
let ?base = 10 in addBase 5
The problem here is that let-bindings are recursive and
introduce polymorphic bindings, which doesn't match well
with the semantics for implicit parameters. This is
aggravated by the ability to mix bindings of implicit
parameters with standard variable and function bindings.
These problems could be avoided by defining that a let
binding either exclusively contains implicit parameter
bindings or none at all. Moreover, all implicit parameter
bindings are non-recursive and not subject to
generalisation during type checking.
This is a simple and workable solution, but some people
feel that merely having a `?' on the lhs of bindings
provides to little visual clue for a semantics that
deviates quite strongly from standard `let' bindings.
[Add a special identifier]
To emphasis the difference in semantics, we may add a
special identifier[1] after the let, such as
let dynamic ?base = 10 in addBase 5
or
let nonrec ?base = 10 in addBase 5
where `nonrec' would have the advantage that it might also
be used for explicit value bindings where they are meant
to be non-recursive (just like plain `let' in SML).
The main disadvantage of this solution is that, in
Haskell, some keywords (`let', `case', `where', and `do')
trigger the layout rule and now `dynamic' or `nonrec'
would have to trigger the layout rule, but *only* when
appearing after a `let'. *urgh*
[Use a special binding operator]
An alternative way to emphasis the non-standard semantics
for `let's containing bindings for implicit parameters
would be to use a binding operator other than `='; for
example,
let ?base := 10 in addBase 5
Unfortunately, `:=' is currently a valid infix constructor
symbol and making it into a reserved operator is probably
as bad as making `with' into a reserved identifier. On
the other hand, like the `nonrec' special identifier, the
syntax could also be used for explicit value bindings.
Are there other, better alternatives for the binding
operator?
There was also a mention of extending the implicit parameter
story to work as recursive bindings, but I don't know any
details here. It would certainly strengthen the case for
using plain `let'.
In Summary
~~~~~~~~~~
We want implicit parameters, but we also want to avoid
turning common identifiers into keywords, thus, stealing
them from library designers. It seems best to render
bindings of implicit parameters as a variant of `let'
bindings. The question is, which syntax do we choose for
this.
The arguments and examples in this message are a summary of
mail messages and verbal discussion between Marcin
Kowalczyk, John Launchbury, Jeff Lewis, Simon Marlow, Erik
Meijer, Sven Panne, Simon Peyton Jones, Alastair Reid, and
my humble self.
Cheers,
Manuel
[1] A special identifier is *not* a keyword. It is special
only in some contexts (in this case, after a `let').
Everywhere else, it can be used as a normal identifier.