Open baronfel opened 8 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.
@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.
@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 examplex.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.
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.
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.
@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.
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.
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.
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 :)
@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.
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.
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
@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 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 meaningf x = fun x -> x
vs the current meaningf 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
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.
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.
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 allowingfun 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... : ()
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.
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.
we treat the first x
as the "argument", "parameter" or "placeholder" implicitly
x -> x + 1
x => x + 1
the "argument", "parameter" or "placeholder" is explicitly defined with \
or fun_
keyword
\x -> x + 1
fun x -> x + 1
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 makingfun
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
, andfunction
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.
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.
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.
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 ;)
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.
fun x -> x + 1
keeper and strict (Explicit)x -> x + 1
cleaner but ambiguous (Implicit)\x -> x + 1
cleaner and strict (Explicit)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,
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.
@stken2050 re this
Why do we need an RFC in order to write down Lambda expressions??
Every language feature needs a technical design document.
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.
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
, andfunction
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.
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 offun
. 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."
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.
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)
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).
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.
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.
ignore what I said, I misread the recent posts.
@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)
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?
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, .
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?).
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;
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
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.
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.
@stken2050 what about (x y z -> x + y + z)
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
@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?
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
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)
.
@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
.
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.
@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 function
xwill 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.
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.
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