dfinity / motoko

Simple high-level language for writing Internet Computer canisters
Apache License 2.0
517 stars 97 forks source link

Passing operators as function arguments? #816

Open paulyoung opened 5 years ago

paulyoung commented 5 years ago

I created the following functions to work around not being able to pass the operators as arguments.

https://github.com/dfinity-lab/motoko/blob/8977c86eedbe96131106d742e53663aaa1c0ca3f/stdlib/int.mo#L4-L6

https://github.com/dfinity-lab/motoko/blob/8977c86eedbe96131106d742e53663aaa1c0ca3f/stdlib/nat.mo#L5-L7

https://github.com/dfinity-lab/motoko/blob/8977c86eedbe96131106d742e53663aaa1c0ca3f/stdlib/text.mo#L2-L4

This way they can be used with Array.foldr<Text, Text>(Text.append, "", xs) and Array.foldr<Nat, Nat>(Nat.add, 0, xs)

Is there currently a way to, or do we expect to be able to do, something like Array.foldr<Text, Text>((#), "", xs) and Array.foldr<Nat, Nat>((+), 0, xs) at some point?

nomeata commented 5 years ago

Currently there is no way, although you can at least use an anonymous function and write

> func f(g : (Text, Text) -> Text) : Text = g ("foo","bar");
let f : ((Text, Text) -> Text) -> Text = func
> f(func (x,y) = x # y);
"foobar" : Text

Note that type inference actually allows you to write the lambda without extra type annotations.

Whether we want a shorthand like (#) or op # is up for @rossberg to decide, but don't hold your breath – the anonymous function form isn’t too bad if you compare it to the right other languages…

rossberg commented 5 years ago

I'd be fine with adding (<op>) as sugar for lambdas. The main problem, though, is that they are syntactically ambiguous when the same operator can be either unary or binary -- e.g., what's (-)? Both Haskell and Ocaml use some really ugly hack for that distinction, which only applies to -, but Mtk has more ops like that. I suppose we could make the short form core syntax and perform some form of overloading for it, but that's a bit less pretty.

nomeata commented 5 years ago

~deleted, misread something~

nomeata commented 5 years ago

I guess saying (★) is, by construction always the binary form is one option.

Do we then also want sections? (★ 5)?

rossberg commented 5 years ago

It would be a bit odd if there was a shorthand for binary but not unary. One way to support both are mixfix forms like (- _) and (_ - _) -- I think Scala does this. That would also more naturally extend to sections, though sections are a bit questionable in a language that does not even have currying (and rare anyway).

nomeata commented 5 years ago

Sections might also be tricky with regard to evaluation order: does (_ + foo()) call foo() now and once, or does it call foo() only when (_ + foo()) is applied?

kritzcreek commented 5 years ago

Scala allows the _ in all kinds of places which can have confusing/surprising consequences so it's generally considered a mistake in their community. In PureScript we added the section syntax for specific syntactic constructs with a straight-forward desugaring.

For example (_ + f x) = \$1 -> $1 + f x and { foo: _, bar: _ } = \$1 $2 -> { foo: $1, bar: $2 } and we've generally been happy with those. (Another commonly used one replaces Haskell's LambdaCase)

case _ of ... = \$1 -> case $1 of ...

nomeata commented 5 years ago

So in PureScript (_ + f x) = \$1 ->( $1 + f x) and not (_ + f x) = let y = f x in \$1 ->($1 + y)?

In partiuclar, (f x + _) ≠ (+) (f x)?

I guess the conclusion is: Let’s not do sections for now.

So back to the question of how to deal with (-) being either binary or unary?

kritzcreek commented 5 years ago

Yes to all of those.

If you had sections (- _) could be the unary one and (-) or (I don't like this one) (_ - _) the binary one.

EDIT: I just saw Andreas already mentioned all of those... don't mind me