[Nickle]'function' considered harmful

Keith Packard nickle@nickle.org
Wed, 22 May 2002 23:50:15 -0700


We've lived with the bogus 'function' keyword entirely because
I couldn't figure out how to factor the grammar to parse the
language without it.  Well, I've figured it out.  The key was
to make the type declaration of the function mandatory, and
then to tie the function name and the following open paren
together in the same production; this leaves the
parser able to follow the two parallel productions:

var:	decl name optinit { ',' name optinit ... }
func:	decl name '('

and reduce when it hits '(', '=' or ','.  I don't know why this
didn't occur to me years ago, but my intuitive understanding of LALR
grammars doesn't quite encompass all of the subtleties yet.

To keep from breaking all existing code, I allow 'function' to
appear in the same contexts that it used to, but it's no longer
necessary.

I've also elminated the bogus parens around anonymous lambdas; getting
rid of the introduces a reduce-reduce conflict in the grammar:

	i = *int func (int i) { ... }

This is either:

	i = *(int func (int i) { ... })

or:

	i = (*int func (int i) { ... })

I think I could refactor the grammar to generate what we want, but it was 
significantly easier to reorder the grammar rules to parse this according 
to the second example.  Like the yacc manual says, refactoring the grammar
usually makes it harder to read and slower to execute (not that I care 
much about parsing speed at that level).

Here's a polymorphic accumulator generator:

	poly(poly) foo (n) { return func(i) { return n += i; }; }

And some applications:

> string(string) s = foo("")
	func(i)
	{
	    return n += i;
	}

> s ("hello")
	"hello"

> s (" world")
	"hello world"


> int(int) i = foo(0)
	func(i)
	{
	    return n += i;
	}

> i(1)
	1

> i(2)
	3

This could obviously benefit from parametric polymorphism, but that's 
still unresolved.

-keith