fsharp / fslang-suggestions

The place to make suggestions, discuss and vote on F# language and core library features
344 stars 21 forks source link

Make the `fun` keyword optional #168

Open baronfel opened 8 years ago

baronfel commented 8 years ago

Submitted by Jorge Fioranelli on 3/21/2014 12:00:00 AM
271 votes on UserVoice prior to migration

Make the `fun`` optional. Otherwise it is more verbose than C#.

Original UserVoice Submission Archived Uservoice Comments

abelbraaksma commented 2 years ago

@cartermp that seems like a good suggestion. Still wondering a bit what exactly @dsyme had in mind when this got approved-in-principle (I'm assuming: "just" make it optional, find the ambiguities, and add bunch of examples), but this would lead the way to an RFC proposal.

dsyme commented 2 years ago

@abelbraaksma When I approved the issue, what I had in mind was literally allowing fun to be elided, so


    (x -> x + 1)

    (x y -> x + y)   

    ((x, y) -> x + y)

That is going to be technically challenging to implement and when I've taken a look it's just hard to get the left-hand patterns parsed as patterns, so instead they need to be parsed as expressions, but that cascades to other ambiguities in other places where -> is used. It may be tractable to work through these, I'm not sure.

Another feasible design is


    (x => x + 1)

    (x y => x + y)   

    ((x, y) => x + y)

and so on. Importantly, => would likely have higher precedence than ,, so for example

    x.Thing(x => x + 1, y => y + 1)

It's arguable it would be ok to make this addition to F#, given that => is now such a standard thing in programming languages, but it's a bit strange to have multiple arrows.

vzarytovskii commented 2 years ago

@abelbraaksma When I approved the issue, what I had in mind was literally allowing fun to be elided, so


    (x -> x + 1)

    (x y -> x + y)   

    ((x, y) -> x + y)

That is going to be technically challenging to implement and when I've taken a look it's just hard to get the left-hand patterns parsed as patterns, so instead they need to be parsed as expressions, but that cascades to other ambiguities in other places where -> is used. It may be tractable to work through these, I'm not sure.

Another feasible design is


    (x => x + 1)

    (x y => x + y)   

    ((x, y) => x + y)

and so on. Importantly, => would likely have higher precedence than ,, so for example

    x.Thing(x => x + 1, y => y + 1)

It's arguable it would be ok to make this addition to F#, given that => is now such a standard thing in programming languages, but it's a bit strange to have multiple arrows.

Been thinking to suggest the same - introduce fat arrow instead of changes to ->, since it'll likely be a rabbit hole implementation-wise, and may have unexpected consequences in existing code.

dsyme commented 2 years ago

An alternative would be to only allow relatively limited (x y -> ...) that could be detected by lookahead in some way, to get around any parsing problems.

e.g. no "serious" patterns like list, records, maybe just type annotations, e.g.

((x: int) (y: int) -> ...)

and little else. Surely that's implementable somehow - the -> is not inherently ambiguous here. Then if you want complex patterns you use the existing fun, and function when you want multiple cases.

cartermp commented 2 years ago

Another minor benefit of going with => is that C# lambda code from somewhere on the internet can be copied over and work. Today we have code fixers in VS and Ionide that try to "fix that up" for users and they could get deleted too.

dsyme commented 2 years ago

@cartermp Yes, though the fact that F# function types use int -> int and yet the simplest lambda syntax would be x => x + 1 is a bit much for me if we can possibly avoid it.

cartermp commented 2 years ago

Yeah, I would personally only be in favor of using x -> y to elide the fun. Have two syntaxes is complexifying, even if there are some additional benefits.

gusty commented 2 years ago

Wouldn't be a breaking change? What about current libraries defining => as operator ? I think I saw some of them, Deedle is the first that comes to my mind.

benjamin-thomas commented 2 years ago

Hello!

I've stumbled upon this thread because I'm learning F# and was looking for a shortcut to define lambdas.

IMO, you should consider having the backslash sign \ (which is lambda looking) being synonymous to fun<SPACE>. This is what Haskell and Elm do. Considering these languages share quite a few idioms/constructs with F#, I feel it makes sens to make a note of it.

-- Haskell
(\x -> x + 1) 1           -- 2
(\x y -> x + y) 1 2       -- 3
(\(x, y) -> x + y) (1, 2) -- 3
-- Elm
(\x -> x + 1) 1           -- 2
(\x y -> x + y) 1 2       -- 3
(\(x, y) -> x + y) (1, 2) -- 3

So to sum up, this:

1 |> (fun x -> x + 1)

Could becomes this:

1 |> (\x -> x + 1)

To eventually become this:

//was (\x -> x + 1)
let inc x = x + 1

I like this symmetry, and I find it make a lot of sens! Although in the end, we are only saving 3 characters. Still, it'd be nice to have :)

abelbraaksma commented 2 years ago

@benjamin-thomas yes, that would have been a good idea, but in this very long thread it was considered and ultimately dismissed on the grounds of not wanting to have two ways of doing exactly the same thing. Also, it’d break too many things.

In a way, optionality of fun does that too, and as the latest comments have shown, we won’t be able to unambiguously remove it completely. It’ll just be a convenience to shorten certain lambdas.

for a shortcut to define lambdas

PS: don’t forget currying. They don’t need fun at all.

benjamin-thomas commented 2 years ago

Thanks for the feedback @abelbraaksma, I wasn't sure if any decision had been made here.

I hope the final solution won't hurt readability since if something is ambiguous to a parser, it's probably ambiguous to a human reading the code too and that'd be a shame.

lucasteles commented 1 year ago

What about current libraries defining => as operator ?

I think that any defined operator => would have precedence and shadow the lambda operator when defined

This would be really good syntax sugar, so I feel like it is worth

abelbraaksma commented 1 year ago

@lucasteles The operator is already defined in F# Core. Code like f x = x => x is valid currently. How would you deterministically disambiguate between that snippet meaning f x = fun x -> x vs the current meaning f x = (=>) x x?

lucasteles commented 1 year ago

@lucasteles The operator is already defined in F# Core. Code like f x = x => x is valid currently. How would you deterministically disambiguate between that snippet meaning f x = fun x -> x vs the current meaning f x = (=>) x x?

Ow I see, I did not know that it already exists, It is not described in the Symbol and operators

Maybe would be possible to determine if the lambda argument type has the operator op_EqualsGreater defined, for this and inline functions cases could not allow the => as lambda, and require the use of the fun -> ...,

Looks acceptable, since this would hit a very small portion of the daily code

ken-okabe commented 1 year ago

let inline f x = x => x is reallly confusing redundant syntax, and I would really like to avoid.

The Haskell/Elm syntax \x -> x + 1 is good and so far, has been suggested by multiple individualas including me, and I don't see any obstacle to apply to F#, but for unknown reasons, it seems that the primary developer of F# and some people here don't like this idea.

I also never have seen any serious discussion as @abelbraaksma mentioned.

in this very long thread it was considered and ultimately dismissed on the grounds of not wanting to have two ways of doing exactly the same thing. Also, it’d break too many things.

If the primary developer likes this idea, since it's so easy to implement without breaking anything (only parser layer replacement: \fun_ ), it could have been done long ago, but the reality is not, and I am not willing to debate this because ultimately, I think this is a matter of personal preference. The bottom line is, I just think there is no rationale on this.

abelbraaksma commented 1 year ago

I just think there is no rationale on this.

If with ‘rationale’ you mean ‘technically it’s possible’, you’re right.

But in language design, decisions are made on other rationales as well, and one of the main ones in many languages is: there should be one, clear, unambiguous way to declare something. F# is based on OCaml, not on Haskell. So the decision here was made prior to F# 1.0 to use fun x -> …. I assume OCaml chose this for its proximity to the mathematical f(x), but I’m guessing.

If at the time it was decided to use \x -> …, we would now have a discussion with proponents of adding more clarity by allowing fun x -> … syntax and they would likely use the same arguments you have.

The rationale here, ultimately, is the strongest there is: don’t add things to the language to do X, if that feature is already explicitly supported. There’s zero gain in doing so, other than adding cognitive overhead to anybody reading code that has mixed style lambdas. That’s a very bad place to be in.

I also never have seen any serious discussion as @abelbraaksma mentioned.

There are 72 hidden comments in this thread. And there have been older issues, now closed, about allowing multiple syntax variants for the same thing, including Haskell style functions.

There’s no harm to rehash the arguments, we can’t expect everyone to know the full history. It’s likely to come up from time to time again anyway, and there’ll always be people that think that multiple ways of doing one thing are good. I’ve been in that boat myself from time to time.

and I don't see any obstacle to apply to F#, but for unknown reasons, it seems that the primary developer of F# and some people here don't like this idea.

You’re correct, some people don’t like it. I’d guess that’d be the majority of F# devs, but I didn’t tally and I could be wrong. But this is not a ‘majority rules’ situation. The F# language (any language for that matter) needs to be protected from becoming a grabbag of new features and syntax flavours.

Luckily, such protection exists. It’s not always easy, and we won’t always please everyone, but in the long run, the language will remain sound and true to its syntax flavour.

let inline f x = x => x is reallly confusing redundant syntax, and I would really like to avoid.

Correct, which is why won’t go that route. We cannot allow this to mean one thing in one context, and another thing in the other.

This here proposal is “make ‘fun’ redundant”. Let’s focus on that. As mentioned in the discussion above, it’s possible for single arg scenarios in a (probably) deterministic set of use cases.

ken-okabe commented 1 year ago

Sorry for not responding sooner. @abelbraaksma

The rationale here, ultimately, is the strongest there is: don’t add things to the language to do X, if that feature is already explicitly supported. There’s zero gain in doing so, other than adding cognitive overhead to anybody reading code that has mixed style lambdas. That’s a very bad place to be in.

I agree on this matter very much, and having said that, we also should realize "Make the fun keyword optional" does let the language have mixed style lambas. Yes, the fact is, the major reason we are struggling here is that we try to have another style to express lambdas. I think it's rather obvious now how complicated this task is, and that is where I say there is no rationale on this direction.

F# is based on OCaml, not on Haskell. So the decision here was made prior to F# 1.0 to use fun x -> …. I assume OCaml chose this for its proximity to the mathematical f(x), but I’m guessing.

Another fact is, the style inheritance from OCaml for lamda expression is not loved by the most of people here, and if we follow the principle not to have mixed style lamdas, we really must give up to "Make the fun keyword optional".

In our reality, as a consequence not giving up, the confusion and implementation difficulties here.

If at the time it was decided to use \x -> …, we would now have a discussion with proponents of adding more clarity by allowing fun x -> … syntax and they would likely use the same arguments you have.

I disagree. It's simply a replacement of syntax letters, \x or fun x. Unfortenately for F#, fun x is not loved, and fortunately for Elm/Haskell, \x is loved, they don't need an extra fun x style for their language. Elm/Haskell guys are satisfiled with their \x style, and never discussed "Make the \ keyword optional" matter.

So, I'm prettey sure we are going to have the mixed style lambdas (optional fun keyword), and like @benjamin-thomas mentioned

I hope the final solution won't hurt readability since if something is ambiguous to a parser, it's probably ambiguous to a human reading the code too and that'd be a shame.

and I also observe you already have proven the concern https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-731874350 (2 Years ago... : ()

abelbraaksma commented 1 year ago

I should probably emphasise again that this issue is approved-in-principle with respect to making fun optional. That’s it.

All that’s needed is an RFC and an implementation.

Still, let me address some of your points for posterity:

Unfortenately for F#, fun x is not loved, and fortunately for Elm/Haskell, \x is loved, they don't need an extra fun x style for their language.

That’s in the eye of the beholder. Some love it, some don’t. That’s the way of the world. When you come from language X to language Y there are bound to be differences. I personally dislike \x -> … very much. It’s distracting and unclear. It’s why I stopped coding in Haskell. But we’re not talking about Haskell here and we shouldn’t try to make F# look like Haskell. Different beasts, different packages.

Fortunately for F#, fun x -> … is very clear and stands out, making it easier for newbies to understand what’s going on.

I'm prettey sure we are going to have the mixed style lambdas (optional fun keyword)

Correct, as was already decided in this thread before.

Another fact is, the style inheritance from OCaml for lamda expression is not loved by the most of people here,

I’m sorry, but I fail how that’s a ‘fact’. Unless with ‘here’ you mean the two or three people suggesting introducing Haskell syntax into F# syntax.

if we follow the principle not to have mixed style lamdas, we really must give up to "Make the fun keyword optional".

Making something optional is not (quite) the same as adding completely new syntax. It’s more like allowing users to drop a certain keyword where it’s clear it’s not absolutely necessary. Just like you don’t have to write end everywhere anymore.

All that said, the main use case here is simple: many (if not most) come to F# from a C# background. A lambda without fun is closer to the fat arrow syntax they’re already used to. Adopting this has real benefits in lowering the bar to understand F# code, plus the benefit of dropping something that’s lexically not absolutely necessary.

Adding a completely different style from a different language does not have that effect. If anything, it will just confuse more.

ken-okabe commented 1 year ago

I personally dislike \x -> … very much. It’s distracting and unclear. It’s why I stopped coding in Haskell.

I really don't understand what is so distracting and unclear.

Implicit style

we treat the first x as the "argument", "parameter" or "placeholder" implicitly

Explicit style

the "argument", "parameter" or "placeholder" is explicitly defined with \ or fun_ keyword

The explicit style used in Haskell/Elm and F# is identical and just syntax letter is different and replaceable.

As you have illustrated in https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-731874350 , the unclearness or ambiguity comes from Implicit style that is "the optional fun keyword"; On the otherhnd,\x style works even in your example because it's just a replacement of the explicit keyword (fun_ to \).

I demonstrate this in my own VScode extension (although I found this is somewhat hard to use realworld coding, but works as the proof of concept) https://marketplace.visualstudio.com/items?itemName=KenOkabe.lambda-for-fun-fsharp

Fortunately for F#, fun x -> … is very clear and stands out, making it easier for newbies to understand what’s going on.

If you can insist fun x -> … is very clear and stands out, \x -> … should be also clear and stands out more concisely.

Making something optional is not (quite) the same as adding completely new syntax. It’s more like allowing users to drop a certain keyword where it’s clear it’s not absolutely necessary. Just like you don’t have to write end everywhere anymore.

Again, this is simply against the principle not to have the mixed style lambdas. A complicated approach to have both Implicit style most of the times, then Explicit style sometimes .

On the other hand, \x -> … is always Explicit style. Sure you have 2 ways to write the identical mathmatical instance/identical lamada expression in fun_ or \, but this is much simpler approach and not against the principle not to have the mixed style lambdas.

If you say the decision has been made in language design, OCaml has chosen to use Explicit style lambda expression, and very unfortenately the explicit keyword fun_ is so redundant.

I should probably emphasise again that this issue is approved-in-principle with respect to making fun optional. That’s it.

Yes, but still, in this thread, the \ style has been suggested by 5 individuals including me,

@Rickasaurus commented on Jul 22, 2017 https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-317117391

We don't currently use \ to mean anything, so why not gift it from haskell if fun is too much?

20 upvotes, but ignored...

@AndreuCodina commented on Nov 28, 2017 https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-347300169 @wannado commented on Nov 29, 2017 https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-347754213 @stken2050 commented on Aug 19, 2022 https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-1220741261 @benjamin-thomas commented on Nov 4, 2022 https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-1302633095

and against your statement

in this very long thread it was considered and ultimately dismissed on the grounds of not wanting to have two ways of doing exactly the same thing. Also, it’d break too many things.

I had never seen any serious discussions, so here I am.

I also have read

@dsyme commented on Nov 27, 2018 https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-441745046 @matthewcrews commented on Nov 30, 2017 https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-347926929 @dsyme commented on Oct 28, 2022 https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-1293904614 @dsyme commented on Oct 28, 2022 https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-1293916362

Then if you want complex patterns you use the existing fun, and function when you want multiple cases.

This is the mixed style lambdas, and the complexity has come that Elm/Haskell has never had, but as I've stated earlier, ultimately, I think this is a matter of personal preference. The bottom line is, I just think there is no rationale on this before discussing why \ style is not appropriate here. It is not considered. That's all I'm tryig t so say here.

In Ocaml language design, Explicit style lamda expression has been employed, and in C#, they do x => x + 1 in Implicit style, then now F# will have the mixed style lambdas and even C# does not have such a complexity. I think this is not a good design.

All that’s needed is an RFC and an implementation.

Why do we need an RFC in order to write down Lambda expressions??
A document to tell users "How to write your lambda in our defined mixed style"? That’s a very bad place to be in.

abelbraaksma commented 1 year ago

The explicit style used in Haskell/Elm and F# is identical and just syntax letter is different and replaceable.

Yes, I'm not disputing that. I'm disputing that we should introduce syntax from multiple languages. If this was F# 1.0, choosing \x would've been fine.

To each language their own: Coq has fun ..., Erlang uses fun ..., Miranda has no lambda, PureScript uses \x..., Scala uses fat arrows like C# (and has _ .., which is another accepted proposal here), ML and OCaml use fun ..., Perl uses sub ..., Swift uses thin arrow like fun -> without fun, with {...}, Julia uses thin arrow x -> ... and function x -> ... (i.e., similar to this proposal: in Julia keyword function is optional).

In the end, you pick one, and stick with it. None of these languages mix two styles, some have optionality for keywords though, as also proposed here.

If you can insist fun x -> … is very clear and stands out, \x -> … should be also clear and stands out more concisely.

I disagree. Not to it standing out, but to it being clear. But I admit, that's just my own preference and the cognitive mapping between fun and the word function, whereas \ has no mnemonic.

20 upvotes, but ignored...

Not ignored as it got 20 upvotes! 😆. Yeah, I missed that. Maybe someone should open a proper language suggestion: "Allow Haskell style lambdas next to the existing syntax", so we can do a proper tally. Who knows, maybe I'm totally off and people love having two totally distinct syntaxes to do the same thing.

See also this comment by @dsyme. Tricky to find, but summarizes my points here, quoting from here:

I'm pretty much against the use of \ in operators. Every language I've seen using \ gets harder to read. I will close this as we decided not to do this in F# 1.x for readability concerns.

As such, allowing \x ... it goes against the "affidavit" that's kinda required on every suggestion:

  • [x] This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.

A complicated approach to have both Implicit style most of the times, then Explicit style sometimes .

Yes, that's the tricky bit: is it really worth the effort to allow what you call implicit style? I honestly don't know. You save only four keystrokes. There are already multiple optional ways: let f = function A -> 42 vs let f x = match x with A -> 42 vs let f = fun x -> match x with A -> 42 vs let f (A _) -> 42.

I don't know. I can see the merit, though, in writing List.map (x -> dosomething) vs List.map (fun x -> dosomething). As long as there's no ambiguity. But as you pointed out, it may not be "worth the effort".

ultimately, I think this is a matter of personal preference. The bottom line is, I just think there is no rationale on this before discussing why \ style is not appropriate here. It is not considered. That's all I'm tryig t so say here.

Yes, in the end whether or not you try to merge the style of two distinct languages into one languages is a matter of personal preference. F# tries to be strict with its ML style, but there have been exceptions, including dropping the verbose syntax (i.e. deprecating it).

The discussion comes up every now and then. @voronoipotato gave a good summary in this comment, abridged quote:

It's VERY tempting for us to be implementing a thing in every syntax of every adjacent language, but in the end it's more valuable to be true to ourselves. [...]


I also have read @dsyme commented on Oct 28, 2022 https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-1293916362

Which is the one about going the "minimal change" needed here, which has been approved-in-principle.

Why do we need an RFC in order to write down Lambda expressions??

Every language change goes through an RFC process here. That's just how we make changes. "Making fun optional" has been approved-in-principle, which just means: "anybody in the community can start the work and write the RFC for it".

Not having an RFC for a language change would be a very bad place to be in. We do not some form of formal process.

A document to tell users "How to write your lambda in our defined mixed style"? That’s a very bad place to be in.

I think it is a good place to be in to have a formal process. This thread has never been approved for "mixed style", it has been approved for making the fun keyword optional. Similar, perhaps, but not the same.

I appreciate we have different views on this. There has been 6 years (!) discussion on this topic going back and forth several times. When it comes to style, there will never be a single opinion (compared for instance with choosing whether to add a new function to a library is a much easier discussion 😆).

In the end, someone has to decide whether a change is worth it. If there's enough support (128 upvotes, plus 271 upvotes prior to migrating to Github), it should be considered. This has been considered in depth and now we need to work out the technical details in an RFC, which itself will lead to a targeted discussion.

An RFC itself does not mean it will be implemented. But even if it is implemented, since it is optional syntax (like much other optional syntax already there), people can adopt a preferred style or use Fantomas or FsharpLint to force a certain style within a company.

abelbraaksma commented 1 year ago

PS, @cartermp gave a good summary, quoting from here:

Note that there isn't much agreement that a new syntax is used. This is about making the fun keyword optional in lambdas, which use the -> syntax. We wouldn't want users to have to balance two different syntaxes. Simplifying an existing syntax (which we have done before) is fine though.

vilinski commented 1 year ago

I personally dislike \x -> … very much. It’s distracting and unclear. It’s why I stopped coding in Haskell.

I'm also ready to stop coding in every language with distracting semicolons ;)

ken-okabe commented 1 year ago

Yeah, I missed that. Maybe someone should open a proper language suggestion: "Allow Haskell style lambdas next to the existing syntax", so we can do a proper tally. Who knows, maybe I'm totally off and people love having two totally distinct syntaxes to do the same thing.

Good suggestion, thanks, probably I should do this.

In the end, you pick one, and stick with it. None of these languages mix two styles, some have optionality for keywords though, as also proposed here.

Actually, you missed the major language: C++ or JavaScript. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function They finally introduces lambda expression: x => x + 1 in es6 after AltJS, especially TypeScript had prevailed.

After all, I'm very happy to discuss with you, and observe this rather constructive. One of the finding is "optional" is such a sweet magic term.

People hate changes, we really like to justify to stay where we have been. We can keep using fun keyword as we did, but it will become optional and we can remove this only when we want to do so.

The wisdom such as

we shouldn't add more ways to do the same thing in this regard. You will not gain that much from trying to shorten syntax; only lead to more inconsitent code across large projects.

or

don’t add things to the language to do X, if that feature is already explicitly supported. There’s zero gain in doing so, other than adding cognitive overhead to anybody reading code that has mixed style lambdas.

However, sticking to this principle, as I mentioned earlier, we must give up this change. You said "you pick one, and stick with it", but omitting fun keyword is the change and the lost of the principle after all.

F# tries to be strict with its ML style

Actually, I observe this is a tricky statement as the magic word "optional" is tricky. Removing fun keyword means from Explicit to Implicit for lambda expression. In other words, this action will make the language "less strict". again I think your illustration is great and everyone who is interested in this issue shoud read it through.

https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-731813049

I don't think it's possible to get rid of fun without introducing ambiguities.

https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-731874350

https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-732034144

The other issue is the scoping, as mentioned. fun introduces scope and is fundamental in that. The first two examples above have unclear scope after dropping fun.

The above is rather objective analysis, the below is rather personal opinion,

image

after all, it's like or dislike as you mentioned and I mentioned.

In the end, someone has to decide whether a change is worth it. If there's enough support (128 upvotes, plus 271 upvotes prior to migrating to Github), it should be considered.

If we are going to override the principle with the magic word "optional", we also can say this:

F# has been strict with its ML style fun x -> x + 1

from now, Make the fun keyword optional x -> x + 1 is cleaner but ambiguous (Implicit and no more strict ML style on lambda) sometimes fun need to be used, so mixed style lambda, complicated

also, Make the \ prefix optional \x -> x + 1 cleaner and strict (Explicit), simple in mathmatical structure, single style lambda.

I personally like the third option because I want to keep my code strict without ambiguity. It should be robust because it's simply the replacement of syntax letters, and I don't have to use fun sometimes when it would be needed in complicated situations. After all, I would like to avoid to use mixed style lambda in my code.

cartermp commented 1 year ago

@stken2050 re this

Why do we need an RFC in order to write down Lambda expressions??

Every language feature needs a technical design document.

abelbraaksma commented 1 year ago

because I want to keep my code strict without ambiguity

The suggested change here will only ever make it if it’s unambiguous in its implementation and use. F# has always been strict. If I suggested otherwise, then I’m sorry for that confusion. To prove this, we need a detailed design (RFC) and implementation (or PoC first).

You said "you pick one, and stick with it", but omitting fun keyword is the change and the lost of the principle after all.

There are changes that add completely new syntax to do the same thing, and there are changes that simplify how we’re using the language, without actually changing the syntax. It’s easy to confuse the two, but they’re not the same thing from a language design perspective.

For instance, introduction of _.memberName() was a simplification of existing syntax. As was allowing Stroustrup style curlies, relaxed undentation rules and dropping of required space in Foo< ^t>. Similarly, parentheses are often not required, but nobody ever complains about it. It’s not ambiguous syntax, but knowing when they need to be applied can be perceived as ambiguous to some.

Such changes improve the language. And considering that the vast majority of newcomers comes from languages that use fat arrow notation (nearly similar to ->), plus the already apparent redundancy of fun in many places, makes me actually wonder why we didn’t pursue this more strongly 5 years ago, to ease F# adoption.

ken-okabe commented 1 year ago

The suggested change here will only ever make it if it’s unambiguous in its implementation and use. F# has always been strict. If I suggested otherwise, then I’m sorry for that confusion. To prove this, we need a detailed design (RFC) and implementation (or PoC first).

Are you sure it can cover all the situations of complexity? You also mentioned https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-731874350

Anyway, this was just off the top of my head. With a little effort, I'm sure more would arise. For instance, consider deconstruction syntax on the lh-side of the -> operator, or tupled vs non-tupled arguments.

@dsyme commented on Oct 28, 2022 https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-1293916362

Then if you want complex patterns you use the existing fun, and function when you want multiple cases.


Such changes improve the language.

I wonder that. I really wonder how many people around here really understand how complicated this change is, and in new thread, I would like to warn strongly this aspect.

There are changes that add completely new syntax to do the same thing,

You repeatedly mention "completely new syntax", but the replacement from fun_ to \x is rather "shorter syntax sugar" and you confuse these. In terms of language design, such a change "we need a detailed design (RFC)" is much more fundamental change and complexities come in.

I am not against the optional fun complicated implementation, but I just feel the many of us simply want the shorter synatx, then as I said, I want the third option.

Every language feature needs a technical design document.

I'm talking about complexity.

ken-okabe commented 1 year ago

PS, @cartermp gave a good summary, quoting from here:

Note that there isn't much agreement that a new syntax is used. This is about making the fun keyword optional in lambdas, which use the -> syntax. We wouldn't want users to have to balance two different syntaxes. Simplifying an existing syntax (which we have done before) is fine though.

Thanks, again, I observe the word "optional", but the fact is it's a complicated approach to have both Implicit style most of the times, then Explicit style sometimes.

So an RFC that covers this complexity to simplify an exisiting syntax should be ready.

@21c-HK commented on Sep 16, 2017 https://github.com/fsharp/fslang-suggestions/issues/574#issuecomment-329973119

I am for using \ instead of fun. There is a difference between typing 1 character and be done with it and having to type 4 characters (fun + space).

To the statement: "users to have to balance two different syntaxes", I would say "users just want a shorter syntax and don't want the legacy one."

abelbraaksma commented 1 year ago

In terms of language design, such a change "we need a detailed design (RFC)" is much more fundamental change and complexities come in.

Just to be clear, but every change that isn’t a bug fix, only internal, only documentational, or only a perf enh, needs an RFC. The ‘drop the space in type annotations’ even has an RFC.

I am not against the optional fun complicated implementation

It’s possible that implementation-wise it isn’t that complicated, that’s what needs to be found out. Finding that there aren’t ambiguities and that the change itself is sound may be harder, though.

Tarmil commented 1 year ago

whereas \ has no mnemonic.

I'll just weigh in on this little bit: \ was chosen in Haskell for its (vague) resemblance with the lambda symbol λ.

(No disagreement with your main point though, having multiple keywords for the same thing is not something I'd like to see)

abelbraaksma commented 1 year ago

Haskell for its (vague) resemblance with the lambda symbol λ.

@Tarmil Ah, I never knew that! Well, then now I have my mnemonic 😆. Tx! (still, it's a very ugly operator, but that's just my bad taste lol).

voronoipotato commented 1 year ago

I feel like given that we have a more specific suggestion that is more actionable, this can be closed. Only argument I can think of for keeping it open is that other people might not read the list of suggestions, and open an identical issue. I don't personally see the value at this point, if you need fun removed consider writing let f x = ... which is the same amount of characters as fun x -> .

edit: ignore this, I misread the previous posts.

vzarytovskii commented 1 year ago

I feel like given that we have a more specific suggestion that is more actionable, this can be closed.

Which one is more actionable? This is pretty specific suggestion, which has been approved in principle.

voronoipotato commented 1 year ago

ignore what I said, I misread the recent posts.

ken-okabe commented 1 year ago

@Tarmil Ah, I never knew that! Well, then now I have my mnemonic . Tx! (still, it's a very ugly operator, but that's just my bad taste lol).

This is actually repeatedly mentioned in this thread:

AndreuCodina commented on Nov 28, 2017 benjamin-thomas commented on Nov 4, 2022

the backslash sign \ (which is lambda looking)

and in my closed thread #1231 , too.

Note: \ is from λ (lambda)

ken-okabe commented 1 year ago

It’s possible that implementation-wise it isn’t that complicated, that’s what needs to be found out. Finding that there aren’t ambiguities and that the change itself is sound may be harder, though.

As I've commented in the other thread to your comment:

I understand that it may feel ‘implicit’, but in the end it’s just as explicit as everything else in the syntax.

I am still in favor to \ explicit prefix to every argument and feel solid and readable (also I don't have a mental block against the \ looking).

Having said that, even in the most complicated situations, if fun can be safely removed by parser/compiler, it should be fine, but if a coder is still requested to write fun sometimes depending on the situation, I beleve this scheme will become complicated that the other languages C# or Haskell never had. Or, maybe we just use parenthesis?

voronoipotato commented 1 year ago

I agree, I don't want to remove fun if there are any situations or circumstances where fun is still required. The feature in my opinion shouldn't be shipped unless there's a global auto-fix and an optional warning ready to go. I'm not in favor of a \ prefix, I think operators are hard to google, and a backslash operator could lead to some pain points in regex searching code and other "stringy" code manipulation such as t4 templates, .

abelbraaksma commented 1 year ago

As mentioned by Don’s comment above, and my analysis earlier, the idea right now is to RFC this for a shorthand for fun x -> y becoming x -> y. There’s no consensus, nor approval, for fun x y -> z becoming x y -> z, nor (IMO) should there be, for the simple reason it’s ambiguous (x itself can be a function).

Though an argument could be made, similar to Stroustrup brackets or precedence, that sometimes it can be applied, and sometimes, where there’s ambiguity, it needs either parentheses or arg reordering. But we gotta start somewhere, so single arg functions is what we should focus on.

Someone should just pick up the gauntlet and spec this out in an RFC. This will allow more targeted discussion on what is, and isn’t possible, and how hard it’ll be.

Which one is more actionable? This is pretty specific suggestion, which has been approved in principle.

@vzarytovskii, afaik, the single arg function approach was approved.

This is actually repeatedly mentioned in this thread:

Thanks. I’m active in many threads and I don’t always remember everything 😆

but if a coder is still requested to write fun sometimes depending on the situation

That shouldn’t be a blocker. Parentheses for precedence or args, indentation, scopes, type annotations, disambiguation of record field names, ‘class end’: you name it. Almost every feature comes with “sometimes the compiler needs some help”.

There’s no shame in that. Be concise where you can or where it improves readability, be verbose otherwise.

don't want to remove fun if there are any situations or circumstances where fun is still required.

That’s impossible, as with nearly every feature in F#. If that’s a requirement, we can’t add many things. This is already true for something simple like |> in comma separated named args. Sometimes you have to be explicit. Just like with F#’s type inference.

The feature in my opinion shouldn't be shipped unless there's a global auto-fix and an optional warning ready to go.

Good point, but I feel that’s for Fantomas and FSharpLint to implement when the time comes. Afaik, no new feature has ever been introduced with a switch to clean existing code (might be nice though, perhaps a new community effort?).

voronoipotato commented 1 year ago

If fun is required for every multi argument lambda, we will see x -> y -> x + y everywhere, without a doubt. It doesn't really affect my readability at all which is part of why I expect to see it, but I feel like we should at least talk to some new users and see if they can read the expected usage better than fun x y -> x + y. If the language feature exclusively benefits experienced devs, I'm okay with passing on it entirely since the main value proposition to me was making it easier to read for newbies who balk at "fun". I'm okay with it if there's a way forward that involves parentheses, but I'm not okay with it if we still need to type "fun" sometimes because people will just chain the lambdas and I'm not confident that is more readable . In my experience, people will stop at nothing to not type fun. Hopefully that makes my objection a little clearer. imo, for those wanting \ for lambda, you can use a ligature or extension for fun -> λ and a shortcut like right-shift+L -> "fun" .

TLDR;

abelbraaksma commented 1 year ago

I know there are 138 comments already and I can’t expect people to read them all, but your points have been addressed before. I’ve tried to summarise it, but essentially anything in F# (or any other language) that’s optional, is usually optional for the simple reason that sometimes it’s required, oftentimes it’s not.

That’s just the way of programming languages and it’s a little bit more true for F#.

Dropping fun in the obvious cases will inevitably lead to -> becoming an operator with precedence rules (in fact, in a match or for..->, fun is already absent, and precedence rules apply). Which means that .. -> .. -> .. will be possible. Parentheses will allow any current expression to be written without fun I’d reckon, but it remains to be seen if it’ll never be required. This, if true, in no way diminishes the use case, as this is already true for every feature that has ‘optional’ in it (think of interface end or yield, both required sometimes, but not in the general case).

Readability for newcomers: anyone from C# or a language using fat arrows will consider this more readable, in my experience. It’s one of the most common questions I get while giving onboarding/beginner courses.

And please, let’s drop the Haskell syntax \x discussion here. It belongs in this thread instead: https://github.com/fsharp/fslang-suggestions/issues/1231

voronoipotato commented 1 year ago

I agree with the dropping the haskell syntax discussion here for sure. If we are confident that it's overwhelmingly non-ambiguous with parentheses, I support it. I don't need literally 100% but I really wanted to avoid seeing x -> y -> z -> x*y+z as much as is reasonably possible.

ken-okabe commented 1 year ago

I don't need literally 100% but I really wanted to avoid seeing x -> y -> z -> x*y+z as much as is reasonably possible.

Just note, there is no reason to avoid x -> y -> z -> .... This is Currying and lambda calculus then related to Combinatory logic.

Happypig375 commented 1 year ago

@stken2050 what about (x y z -> x + y + z)

vilinski commented 1 year ago

I don't need literally 100% but I really wanted to avoid seeing x -> y -> z -> x*y+z as much as is reasonably possible.

Just note, there is no reason to avoid x -> y -> z -> .... This is Currying and lambda calculus then related to Combinatory logic.

really? technically yes, but alot less key strokes without

ken-okabe commented 1 year ago

@Happypig375 @vilinski Sorry, I should have been careful to mention this. There is no reason to avoid x -> y -> z -> ...., yes, this is in theory; lambda calculus is based on unary functions.

However, conveniently, as a syntax sugar in F#, I suppose, we can write the identical mathematical entity as,

// Lambda expressions with parameter lists.
fun x y z -> ...

and since there is no syntax sugar in the type annotations, the type is like int -> int -> int -> int, and no one claims that reasonably, this type should be avoided.

Then, as proposed here, dropping fun, becomes

// Lambda expressions with parameter lists.
x y z -> ...

should be fine, but for

 (x y z -> ... )

This is not like making fun optional.

Do you suggest lambda without fun always requires the extra ( ) parenthesis?

abelbraaksma commented 1 year ago

Let’s reiterate: we know that dropping ‘fun’ on single-arg functions (which technically is every function), can be done unambiguously. This part is approved in principle, and we gotta start somewhere.

Syntax like a b c -> .., be it with or without parentheses, is ambiguous, esp if a or b are shadowing a variable holding a function value. fun introduces new scope, but parentheses do not, hence it being problematic. Turning this into a -> b -> c -> … would again be unambiguous, as it is the same as fun a -> fun b -> fun c -> ...

The single arg option can relatively easily be introduced without fun by virtue of -> not being an overloadable operator. It’ll require a little look-ahead, but it’s (probably) possible to be fixable in the lexer or parser phase (ie before the TAST is built). Multi-arg require a certain disambiguation, for instance, it may only be uniquely possible if there are no same-named variables in scope.

Also, don’t forget that each lambda argument is a pattern, which complicates potential determinism. Again, as mentioned before, I’d vote for focusing on the part we know is possible and accepted by @dsyme (single arg) and learn from that experience before we introduce the same idea for multiple args (if at all possible to begin with).

Example:

what about (x y z -> x + y + z)

@Happypig375 Won’t work if:

let x a = a * 42
let y = 12
(x y z -> x + y + z)

// function application will turn this into:
((x y) (z -> x + y + z))

// error that (x y) does not resolve to a function itself
// or error that ‘x + y’ is invalid because ‘x’ is a function and ‘y’ is 12
Tarmil commented 1 year ago

The thing, though, is that this is already illegal:

x y fun z -> x + y + z
// error FS0010: Unexpected keyword 'fun' in expression

It's never been possible to pass a lambda as argument to a function without parentheses. So there's no reason that making fun optional should change that. I think it should be possible to make x y z -> x + y + z unambiguously equivalent to (fun x y z -> x + y + z).

abelbraaksma commented 1 year ago

@Tarmil the problem is with function application. It has the highest precedence. Whereas fun has a much lower precedence. Dropping fun in multi arg pattern expressions (which is what args are) leads therefore to a conflict. That’s also why you get an error above, even though fun a -> fun b -> fun c -> .. would’ve been fine. You need parentheses because fun has lower precedence than function application.

Its similar to f x + y. First f x is evaluated, because of higher precedence, then + y.

See https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/symbol-and-operator-reference/#operator-precedence

voronoipotato commented 1 year ago
let x a = a * 42;;
//val x: a: int -> int
let y = 12;;
//val y: int = 12

fun x y z -> x + y + z;;
//val it: x: int -> y: int -> z: int -> int

(fun x y z -> x + y + z) 3 4 5;;
//val it: int = 12
(x y z -> x + y + z) 3 4 5;;
//desugars to (fun x y z -> x + y + z); you'd need parentheses for other reasons anyways. 

@abelbraaksma Everything between ( and -> would be an argument, just like how it is for fun. It does mean there's no way to type funless lambdas without parentheses, but that seems like a preferable choice over x -> y -> z -> x + y + z everywhere. It's just noisy and beginners need a single minute to understand the syntax before being thrown into currying. While currying isn't complex, it is "weird" to people who were trained in OO first like non-js devs.

abelbraaksma commented 1 year ago

@voronoipotato in your example, the issue still isn’t resolved. ( doesn’t introduce scope, fun does. So, ` (space) is still taking precedence. Your code isn’t necessarily ambiguous, but the functionxwill be evaluated first. So, order of precedence is((((x y) z -> x + y + z) 3) 4) 5`.

There may be a way to introduce preprocessing to overcome this issue and invalidate long standing precedence rules, but it’s likely gonna be very hard.

voronoipotato commented 1 year ago

X will not be a function, it just desugars to fun. It will not be hard since it's just syntax sugar where the first paren gets a fun put after it. I just tested this in fsi and i couldn't see how it breaks. When you type ( followed by -> it desugars to (fun followed by ->. There's no room for any function to be run or even interpreted.

Edit, there is one edge case i just thought of which is match statements but feel like you could look for the vertical bar.