#697 Remove '&' curry/partial apply operator

brian Tue 28 Jul 2009

This is a proposal based on #682 to remove the & operator, in favor of just using closures.

Current & operator and its closure equivalents:

&foo        =>  |,| { foo }
&foo(a)     =>  |,| { foo(a, b) }
&foo(a, b)  =>  |,| { foo(a, b) }
&foo(a, b)  =>  |c,d| { foo(a, b, c, d) }

A bit more chars to express the same concept, but I find that after two years I use this operator very little. I think this is a case where we can remove a feature and save some of our complexity budget.

If you care, please give me a yeah or nay.

tcolar Wed 29 Jul 2009

yeah.

can the & be used for field access then ? (don't like the *)

tompalmer Wed 29 Jul 2009

Although & is still already a binary operator.

tcolar Wed 29 Jul 2009

Right kinda forgot that (binary and).

Though i don't think continuing a binary operation after a new line is nearly as commonly done at all.

But yeah i guess it still needs smarts as far as a parse goes.

Note though, there where a few other grammar items that required to be "smart" with newlines anyway. Examples:

indexExpression: if [] is after a newline it's not an index expr.

callParams: () as to be on same line.

typeLitteral: # need to be on same line

and so on

That's what the notAfterEol() stuff is for in my ant grammar (a bit ugly) http://svn.colar.net/Fan/src/net/colar/netbeans/fan/antlr/Fan.g

KevinKelley Wed 29 Jul 2009

If you care, please give me a yeah or nay.

I think mostly I'm okay with this, the only thing is there's a pretty common use case -- using curry to pass a callback function to an event handler -- that's convenient and familiar to probably a lot of people coming to Fan.

On the + side, with both curry and closures, there's the confusion over when to use which and whether there's a difference.

Overall I think I'm in favor of removing curry; then all the places where it's currently used in the Fan distro, will provide an intro to closure usage for those (like me) that haven't spent a lot of time in languages that use it.

qualidafial Wed 29 Jul 2009

+1

jodastephen Wed 29 Jul 2009

I think this change suits Fan, although the FP lovers will hate the change no doubt.

I also think that this is related to Bound methods/Properties which tackles issues that more commonly crop up in coding.

brian Wed 29 Jul 2009

I think this change suits Fan, although the FP lovers will hate the change no doubt.

True, but I don't think FP lovers like Fan to begin with

And it frees up & for a prefix operator. True it is also a binary operator, but it was one that was never used when not on the RHS of an assignment or as argument to a method because it wasn't a complete statement otherwise.

jodastephen Wed 29 Jul 2009

Oh, I'm in favour of the change, but have a feeling that something very similar will return with proper properties/bound methods.

brian Wed 29 Jul 2009

Oh, I'm in favour of the change, but have a feeling that something very similar will return with proper properties/bound methods.

I agree, but now we will be free to develop that feature without worrying about how it interacts with curry operator.

Any other yeahs or nays?

helium Wed 29 Jul 2009

yeah

alexlamsl Wed 29 Jul 2009

Oh Yeah.

Edit: May I ask humbly what is FP in the conversation above?

brian Wed 29 Jul 2009

Functional Programmers (at least that was what I thought we were talking about)

freddy33 Thu 30 Jul 2009

+1 The & is quite confusing and the closure syntax very clear. I just modified our parser for the * operator for field access. Conclusion: I don't like it, and & will feel/look a lot better.

brian Sun 9 Aug 2009

Promoted to ticket #697 and assigned to brian

brian Sun 9 Aug 2009

OK, here is the major problem with removing the curry operator today...

Today there is a subtle (and one might say confusing) difference between how a closure and curry binds its scope. A closure binds local variables as true variables, but curry binds strictly by reference at the time the curry is created.

Consider this program:

x := "alpha"
closure := |,| { echo(x) }
curry := &echo(x)
closure()
curry()
x = "beta"
closure()
curry()

This will print:

alpha  // closure
alpha  // curry
beta   // closure now uses new value of x
alpha  // curry bound to what x was originally

The reason this is important is because it determines the immutability of the function. In the code above closure is mutable, but curry is immutable.

This problem must be solved in order to allow closures to replace curry everywhere they are used.

The most obvious solution is to duplicate Java's use of final keyword on local variables. But I'd like to hear other ideas.

KevinKelley Sun 9 Aug 2009

I was wishing for final the other day, trying to figure some way to pass a closure to an actor so it could do tick callbacks to a window, and I kept getting not immutable errors.

Ended up figuring out I could get a curry to work -- without quite understanding that this is why.

So:

final w := (Widget)this
Ticker(20ms).send(|,| { w.repaint })

where ticker calls the func then uses sendLater(delay) to send it back to itself.

andy Sun 9 Aug 2009

A keyword modifier seems natural here (I assume you will use const here vs final). Its a bit more verbose potentially, but given how little this use case has popped up, seems fine. Plus its useful beyond the curry/closure case.

JohnDG Sun 9 Aug 2009

const means the object pointed to by the reference is immutable and cannot change. final means the reference is immutable and cannot be reassigned to a different instance. Each refers to a different thing, and const final has a meaning unique from either const or final alone.

andy Sun 9 Aug 2009

You can make that distinction, but Fan does not already. A const field implies both. So I would assume we would extend the same to local variables. I think its much simpler to use one term, even if its not 100% technically correct.

KevinKelley Sun 9 Aug 2009

In Fan terms once might be appropriate for this.

Or, is there a way to avoid adding to the keyword load by letting closures be immutable if they don't do anything stupid? I don't know exactly what that would mean though.

brian Mon 10 Aug 2009

Well both final or const would solve this particular problem. Final (or once) locals with semantics of Java would mean the reference cannot be reassigned - that would allow me to optimize heap local variables similar to how inner classes work today in Java (although Fan seems to be running pretty darn fast even with heap based locals).

Const would be a stronger guarantee than final, but has the ugly problem that it leaks into the signature:

Void m(const Str[] m)

If we ever did support const params/locals, then I would expect that signature leaked into the call site as:

m(list.toImmutable)

But after some more thought I think we can avoid all this ugliness and complexity and follow the same convention as I already use in List and Map by just having a Func.toImmutable method.

In fact, I am actually going to rework things a bit and put this as a method on Obj:

class Obj
{
  This toImmutable()
}

It will continue to work as is for List and Map, and closures will do something similar by calling toImmutable on all their bound arguments. This actually will plug up a hole I've been meaning to fix in the const type system when working with const Obj fields.

tcolar Mon 10 Aug 2009

Nice ! I actually was thinking about how it would be cool to have toImmutable available globally few days ago.

KevinKelley Mon 10 Aug 2009

Perfect. Hg pull.

brian Wed 4 Nov 2009

Ticket resolved in 1.0.47

The compiler code for the deprecated & operator has been fully removed.

tactics Tue 15 Dec 2009

Found some doc that needs fixing in http://fantom.org/doc/sys/Func.html#method

Method? method()

Return the associated method if this function implements a 
method slot. If this function a is curried method using 
the "&" operator method call syntax, it will return the 
associated method. Otherwise return null.

Login or Signup to reply.