One of the things I'm doing at the moment is re-examining decisions where
future extension of the language might be constrained by current syntax. In
many  cases we can tweak things now so that shifting later won't break
(much) existing code. One of those topics of discussion was curried
application.

To be clear: I'm not contemplating a shift to curried application right now.
I'm merely asking whether/how to keep us [syntactically] open to the
possibility for the future.


Here is my attempted summary of where the discussion stood:

[Pro] Since functional composition is pretty important, curried style
application MAY be an important thing to have.

[Pro] Taken in combination with suitable constraints, curried style
application resolves the varargs problem.

[Tie] Contrary to what I initially believed, it *is* possible to get
predictable register allocation and memory allocation in the presence of
curried application, but this requires shifting to a style in which
multi-argument functions are handled using a single, tupled argument.

[Con] A key issue in curried notation is that arity is not part of type, and
in consequence, the allocation consequences are not readily apparent. That
is: it is not clear to the programmer which applications induce allocation
(indeed, this is intentionally obscured). In a language where some programs
need to explicitly manage heap allocations, this particular bit of
encapsulation may not be a good thing.

[Neither] I proposed that we could silently coerce arities at application
points by silently inserting the necessary lambdas. It was pointed out that
in some cases this would lead to cyclic heap allocations and that it would
impede tail recursion.

[Matter of Preference] I confess that the curry-style application syntax
seems to me less cluttered, but that was certainly not true when I first
encountered it, and it is certainly not what mainstream programmers
encounter daily.

[Pro] *Because* curried application removes arity from function type, it
permits certain type generalizations that are difficult to achieve in a
non-curried language.


The alternative that suggests itself is to have an explicit composition
operator. For example, we could decide that

   f . g

means "perform a partial application of f to argument g".


The concerns, in my mind, that would lead us to avoid currying are as
follows:

1. The curried notation is unfamiliar to our target audience.

2. Because of this, it is fair to expect that curried notation will lead to
a contest of styles in library construction. Libraries built by ML/Haskell
folk will curry. Libraries built by the rest of the world will not, and this
will force cognitive load on users who attempt to use both.

3. Curried application effectively breaks our ability to analyze heap
allocation across a library interface boundary.

4. If arity information is not part of either syntax or type, then the
result of optimizations like arity lifting cannot be expressed within the
language or the type system.


Our current syntax is "mostly extensible" with only very minor changes -
primarily making the parens optional when a single argument is passed. The
one place where there is a future-proofing issue is in the area of type
definitions and constructions. We currently require parens there to maintain
parallelism with the function application syntax. We basically have two
choices there:

  1. Shift to curried application for type constructions immediately.
  2. Make a commitment to support patterns in type definitions.

By "patterns in type definitions", I'm suggesting that the syntax:

  struct S('a, 'b) {
  }

can be seen as a single-argument type constructor that is applied to a type
tuple. The type tuple serves as a pattern match, is decomposed pattern-wise
in the usual way, and its constituent variables are then usable within the
body of the definition. If we choose to see it this way, then the current
syntax is already future-proofed once we deal with the single-argument case.
However, types created now will later appear strange.


So here is our range of immediate choices:

1. Adopt the policy position that heap effects are important, that we need
to preserve the ability to analyze heap effects, and that we therefore will
NOT go in the direction of currying.

2. Defer the issue by making the parens optional on single-argument
applications (assuming I can get that into the parser)

3. Decide that currying is important, and that we should immediately shift
to curried application syntax **for types**.


Scott Doerrie has suggested a clever fourth option, which exploits the
forthcoming Nat kind. He observes that if the type of a procedure embeds a
natural indicating its arity, then the matter of arity analysis (therefore
allocation analysis) migrates from a syntactic analysis to a type analysis,
and we are then free to use the curry-style syntax without loss of
information about allocations. I don't think we've explored this option far
enough to understand it yet, but it seems like a good approach.


My personal opinion is that retaining arity information is crucial - if only
because we need it in the type system for reasons external to BitC. That
said, I think it would be fine to choose to handle that in the type system
rather than in the syntax.


What do people think we should do here?


shap
_______________________________________________
bitc-dev mailing list
[email protected]
http://www.coyotos.org/mailman/listinfo/bitc-dev

Reply via email to