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.