[Haskell-cafe] Haskell's "partial application" (not currying!)
versus Business Objects Gem Cutter's "burning"
Conor McBride
ctm at cs.nott.ac.uk
Wed Jul 4 08:22:18 EDT 2007
Hi Jules
Your explanation of lambda-abstraction, dealing in full generality
with both scope and
multiplicity, is a good one. But it's still interesting to
investigate the possibility
of a privileged notation for linear abstraction, based on leaving
holes in things, by
way of illustrating the design space. I seem to remember this topic
coming up before
(on Haskell Prime?), but I can't find the archival references just now.
On 3 Jul 2007, at 11:19, Jules Bean wrote:
> peterv wrote:
>> In Haskell, currying can only be done on the last (rightmost)
>> function arguments.
[..]
>
>> This burning looks more general to me, but cannot be done using
>> the textual approach?
>
> Well, it can be done, but basically there are two issues:
Dead right.
> 1. You need to demarquate the 'scope' of the ?. What 'lump' of
> expression is the partially evaluated part. An obvious way to do
> this is with parentheses, but you have to be careful.
Let me borrow braces {..} for purposes of discussion. One might
imagine a form of
abstraction where one simply writes an expression in braces,
replacing some
subexpressions with ?, like Haskell Emmental
{? * 10 + ?}
Of course...
> 2. If you have more than one ?, you need to remember which is
> which. Think of nested expressions, nested ?s. What if you want to
> use the 'same' ? more than once?
...we need a convention to handle this. The obvious convention is
that each ? is
distinct and that they (and only they!) are abstracted from a brace
in left-to-right
order. That's to say, the above means
\x y -> x * 10 + y
hence
{? * 10 + ?} 4 2 = 42
It can make sense to allow nested applications inside braces, using
parentheses.
{f ? (g ? x)}
It can even make sense to allow nested braces, provided we adopt the
convention that
a ? is bound by its most local brace.
{foldr {? : ?} ? ?} = flip (++)
For expressions with no ?s, {..} and (..) coincide. {f ? ... ?} means
f. All sorts
of compositions, not just {f (g ?)}, are readily expressed, without
resorting either
to naming or to lurid ascii-art.
[Local notational speculation: use (..) for {..}
One might well wonder: if ?-less {..} are like (..) and ?-ful (..)
are currently
meaningless, why not overload (..) ? This can be done, but it
gives a rather
peculiar semantics to previously innocent syntax---you lose the
ability to add
extra parentheses to clarify grouping, so
(f a) b is not necessarily the same as f a b
and you also lose the ability to nest expressions, except via
precedence
(f (g ?)) means f g and not f . g
(? * 10 + ?) may work, but ((? * 10) + ?) causes trouble.
End local speculation]
Oddly, to my mind, this is exactly the approach that the Gem Cutter
crew take, but
pictorially. On the one hand, they use tree diagrams for expressions,
so they
don't need parentheses for grouping. On the other hand, they
explicitly choose
only to allow the abstraction of "burnt" arguments from the very
application in
which they occur. Correspondingly
{foo ? y} is expressible, but
{? * 10 + ?} has to be expanded.
This seems like a missed opportunity to me.
What should Haskell take away from this?
(1) You know where you are with lambda-abstraction. You can even do
some sorts
of "fast and loose" reasoning under binders, with some hope of
preserving the
meaning of your expressions. By comparison, computing inside {..} is
very
dangerous:
{0 * ?} is not {0}
{const ? ?} is not {?}
{flip f ? ?} is not {f ? ?}
{(\x -> (x, x)) ?} is not {(?, ?)}
(2) Even so, it might be a useful feature. Something of the sort
*has* been
proposed before. I just found it
http://hackage.haskell.org/trac/haskell-prime/wiki/
FlexiblePartialApplication
but without the special {..} bracket, it's a nightmare. Brackets are
always at
a premium in syntax design. What would we do?
(3) Exercise for readers:
implement constructors
P v for embedding pure values v
O for holes
f :$ a for application, left-associative
and an interpreting function
emmental
such that
emmental (P (+) :$ (P (*) :$ O :$ P 10) :$ O) 4 2 = 42
I think the question of whether to support linear abstractions other
than of
an argument suffix is an interesting one. The flip answer is a bad
answer;
lambda abstraction is a good answer, but sometimes feels too heavy
for this
job. I really don't have a strong opinion about whether it's worth
supporting
a lighter notation for the linear case, but I thought I'd at least
try to
inform the debate.
All the best
Conor
More information about the Haskell-Cafe
mailing list