Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

shortcut syntax for partial function application #7351

Open
gavinking opened this issue Apr 13, 2018 · 13 comments
Open

shortcut syntax for partial function application #7351

gavinking opened this issue Apr 13, 2018 · 13 comments

Comments

@gavinking
Copy link
Contributor

gavinking commented Apr 13, 2018

Currently, it's easy enough, but kinda verbose, to partially apply a function:

value formatHex = (Integer n) => Integer.format(n, 16);

The discussion in #6615 raised the possibility of having a shortcut syntax for the special but very common case where we're partially applying a function of multiple parameters and obtaining a function with exactly one parameter.

Basic proposal

The proposed syntax would be, approximately:

value formatHex = Integer.format(it, 16);

Where it is a "magic identifier" or "soft" keyword. (That is, if there is nothing named it declared in the current scope, it would be given this special interpretation. Furthermore, a warning would be produced wherever the name it is declared.)

A reasonable objection to this proposal is that it is much too much of a special case:

  • it doesn't handle arbitrary expressions, and
  • it only handles the limited case of exactly one argument.

I think these objections are reasonably dispatched by noting that:

Possible extension

An open question is whether this feature could be extended to the receiving instance of a method invocation, in contexts where the receiving instance type can be inferred, for example:

strings.map(it.initial(3));

would mean:

strings.map((s) => s.initial(3))

and:

string |> it.initial(3)) |> list.add

would mean:

string |> ((s) => s.initial(3)) |> list.add

I'm not sure about that, but it's worth considering.

@CPColin
Copy link
Contributor

CPColin commented Apr 13, 2018

Would it screw up the language grammar a bunch if we made it so the value keyword in this context acted as the magic identifier? So, for example:

value formatHex => Integer.format(value, 16);

strings.map(value.initial(3));

Or would that get rapidly ambiguous?

@gavinking
Copy link
Contributor Author

@CPColin I'm not sure, I would have to try it. And then I would have to decide if I actually like it ;-)

@gavinking
Copy link
Contributor Author

gavinking commented Apr 13, 2018

I would have to try it

A quick attempt at this choked ANTLR, but I don't have time right now to dig into precisely why.

@CPColin
Copy link
Contributor

CPColin commented Apr 13, 2018

A quick attempt at this choked ANTLR

Well that's a good sign!

I would have to decide if I actually like it.

Yeah, I could see giving an extra meaning to the value keyword having a high potential for confusion.

@gavinking
Copy link
Contributor Author

@CPColin no, "choked" as in resulted in this error:

Decision can match input such as "VALUE_MODIFIER" using multiple alternatives: 1, 2, 3

i.e. it looked ambiguous to ANTLR.

@CPColin
Copy link
Contributor

CPColin commented Apr 13, 2018

Sorry, I should've italicized "that," to make my lame sarcasm more clear.

@jensli
Copy link

jensli commented Apr 14, 2018

I think it is a good idea to be extremely careful with syntactic sugar magic that makes the language less explicit and more compact, because it also makes the language less regular and more tricky to understand.

As a counter-proposal I'd like to suggest this: Infer the lambda parameter type (like in Java), then the example above already is pretty short, and it follows the fundamental pattern of variable declarations better:

value formatHex = n => Integer.format(n, 16);

With the term "the fundamental pattern" I mean that you first declare a variable and give it a name, then you can use it. (this does not follow that pattern, but that's not a reason for breaking the pattern in more places.)

About this suggested syntax

value formatHex = Integer.format(it, 16);

I think this is really problematic! Because the useful syntactic distinction between a lambda expression and a function call expression is lost. These two things are so different so I think it is really useful for readability to have distinct syntax for them.

In this example it is not any more so immediately clear to a reader if formatHex is a string or a function. The reader has to scan the whole expression to see if it appears anywhere in it.

I think this syntax is pretty bad in this simple example, and it quickly become worse in a little more complex cases. For example:

value formatN = (Integer n) => Integer.format(it, n);

This (I think) would be a function which returns a function.

I also think it sounds weird that it would be treated as either a keyword or an identifier by the compiler.

@luolong
Copy link

luolong commented Apr 15, 2018

After seeing the version with soft keyword ‘it’, I tend to agree to a degree with @jensli here that the keyword ‘it’ does not really bring out the fact that we are dealing with a partial application here.

If we stay with keyword approach, I would much prefer it if ‘value’ could be used - along with the keyword like treatment in the rest of the language it is much less likely that it would be confused with a local variable name than ‘it’.

Other than that I do have a preference for using ‘_’ (underscore) as a partial application wildcard. I’ve always seen it as a meaningless sigil to replace some variable name whenever I mean to say “I don’t care what is the input; Just do this”

@gavinking
Copy link
Contributor Author

I think it is a good idea to be extremely careful with syntactic sugar magic that makes the language less explicit and more compact, because it also makes the language less regular and more tricky to understand.

To be clear: I don't plan to implement this feature unless it is extremely clear that it's very popular. The only reason this really came up is that apparently JavaScript is looking at adding something like this. I definitely don't plan to start work on it right now.

As a counter-proposal I'd like to suggest this: Infer the lambda parameter type (like in Java)

That is a good suggestion. In Ceylon (unlike in language like ML and Haskell) we don't typically infer the types of things from how they're used, since that doesn't generally interact very well with subtyping.

However, we've just identified a restricted case in which anonymous function parameter type inference from usages does look pretty robust. If the parameter is only used as a function argument, and never:

  1. as the receiver of a method invocation, nor
  2. in an operator expression, nor
  3. as the argument of an overloaded (Java) method,

then we could reasonably infer its type from how it's used.

I like the idea, and I think we should open an issue for that.

OTOH, it would only get us part of the way to "partial application".

((n) => Integer.format(n, 16))

is still significantly more verbose than:

Integer.format(it, 16)

But maybe it's good enough.

@jensli
Copy link

jensli commented Apr 20, 2018

If a short-cut syntax for lambdas is to be introduced, then I think it is best to make it syntactically different from normal function calls in some easily spotted way.

Maybe this syntax can be used:

value formatHex = => Integer.format(it, 16);

That is, the => token from the normal lambda syntax, but without nothing in front of it.

It looks a bit weird in an assignment, but better as a function argument:

func(=> Integer.format(it, 16));

But I myself think this is too implicit and weird and probably create more confusion that clarity.

@gavinking
Copy link
Contributor Author

Now that we have #7058, I feel like the pressure for this feature is significantly alleviated.

@gavinking gavinking removed this from the 1.5 milestone Apr 23, 2018
@xkr47
Copy link
Contributor

xkr47 commented Apr 24, 2018

For the record, I dislike the keyword it, since I use it for iterators :)

@xkr47
Copy link
Contributor

xkr47 commented Sep 10, 2018

@jensli please see #7190 (comment)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants