Open baronfel opened 8 years ago
+1 to fat arrows, all in the name of driving adoption for the language.
Interesting discussion. I wouldn't support something along the lines of Haskell or Elixir-style lambdas, because instead of shortening what's already there, that would replace it with a new symbol.
I'm also unsure if fat arrows would be needed - is it enough to simply make fun
optional? E.g.
Seq.filter (x y -> something x y)
sticking with ->
leads to situations like
let fn x y =
match x with
| 1 -> z -> x + y * z
| _ -> (z -> x - y * z)
// ^ I guess parens would be required here?
let fn': int -> int -> int = x y -> x + y
I'm ambivalent about this approach, =>
seems better than that
I don't mind fun
though
Good point, =>
would make cases like that unambiguous.
Using an available user-defined operator will break existing code, specifically =>
is being used in some projects, for instance Deedle makes heavy use of it.
The ambiguity reported by @cloudRoutine can be solved since we never match against functions, I mean we can expect a pattern between |
and ->
.
Eventually if a further enhancement allows to decompose a function (if it ever makes any sense) the fun
keyword will not be optional in that context.
Maybe even the function
keyword can be made optional. Cases like
let f x = function | (Some x) -> ... | None -> ...
become
let f x = | (Some x) -> ... | None -> ...
but that for sure will confuse any Haskeller.
I don't like =>
since ⇒ means logical implication.
The mathematical standard is ↦. However there doesn't seem to be a good way to write it in ascii.
->
may be the best ascii approximation of ↦.
~>
doesn't seem too bad if we need something different.
I don't hate this, but I don't love it either. Do you really think the small change will bring over people? I've never heard anyone say "I don't use F# because of the fun keyword". The grammar is also a bit more ambiguous and so difficult to parse, so it will likely always have to be in parens, saving one character on average.
We don't currently use \
to mean anything, so why not gift it from haskell if fun is too much?
I do rather like the idea of dropping the "function" keyword though. It's obnoxiously long.
+1 for this, not only fun
makes us type more it also creates inconsistency in keywords? Why do some have to be a full word (match
) while this one gets abbreviated? Because it's fun?
+1
But more like Scala's lambda shorthand form Seq.filter(_>1)
By the way, proposal of lambda using =>
has already been declined in #384
+1, if it is possible to make fun
optional while still being unambiguous, it would be greatly appreciated.
When I look at my code I see a lot of lets on the left hand side that I think must be redundant. We could remove those too...I'm just joking I think.
I don't like to remove the 'fun' from F#. I would rather discuss about an alternative syntax for very short lambda expressions.
I like the fun keyword because it is easy to read. Sometimes having a lot of symbols make the code harder to read/maintain.
When reading fat arrow or lambda from others language like Javascript, elm or haskell I am always confuse and need time to understand the code.
IMHO, 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. The property accessor from the other issue is kinda ok, but even then even that can lead to more inconsistencies of just doing the same thing.
As @MangelMaxime said, less keywords and more symbols make code hard to read, plus, in editors, the fun keyword with appropriate color can easily tell the structure of code
@TIHan I agree on your point I admit that I'm in general against anything which reduces readability. It's such a strong and important feature of F# which I always use as selling point for beginners! PS: I'm very against this!
I really would like fun
to disappear of course!
Not sure ->
alone would be the best replacement, =>
is easier to read especially considering match
and function prototypes.
The most annoing use for "fun" is calling methods or looking up properties in objects in pipelines (as they can't be curried away).
Kotlin style lambda with implicit "it" binding would be optimal:
{ it.DoStuff() }
{ i -> i + 1}
A completion snippet for fun
and function
in ionide would be enough for me to solve such "problem". No need to make it shorter if it more clear what happens. Haskell's backslash or scala's underscores are nice but they are adding PROBABLY more perl into F#. Thanks anyway!
@realvictorprm Instead of:
(fun x y -> x + y)
Proposal 1:
\(x y -> x + y)
shown with a font ligature:
λ(x y -> x + y)
Proposal 2:
{x y -> x + y}
shown with a font ligature:
λ{x y -> x + y}
Examples
[ 1; 2; 3; 4; 5 ]
|> map λ(x -> x + 2)
|> fold (*)
[ 1; 2; 3; 4; 5 ]
|> map { x -> x + 2 }
|> fold (*)
For me it was unexpected to see so many opinions in favor of keeping fun
, but that's the good thing of open language design: we can get a better understanding of community's opinion :) However, I'd like to see this suggestion implemented so I'll try to lobby a bit for it with some arguments:
myList
|> List.map (fun x -> x + 1)
|> List.filter (fun x -> x < 5)
myArray
.Select(x => x + 1)
.Where(x => x < 5)
fun
could make the code ambiguous (there's one by @cloudRoutine above). In my experience you already have to surround lambdas with parens most of the time (except in declarations/bindings let f = fun x y -> x + y
where you can also use let x y = x + y
), especially when you pass as them as arguments, either curried (see above) or tupled:let foo (f: 'a->'b, x: int) = x
let y = foo(fun x -> 5, 6) // Doesn't compile
async
mechanism to the language). This discussion has been revived because of the suggestion to allow _.Length
for property accessor, but this would fit that and many other cases without introducing syntax for a very specific situation. I paste here the comparison from this comment:xs |> List.map (fun x->x.Length)
xs |> List.map (x->x.Length)
xs |> List.map (.Length)
xs |> List.map _.Length
@AndreuCodina Ligatures are really nice (I've also seen fun
being replaced by λ
in emacs and looks awesome) but personally I don't think they should be taken into account for language design. Nowadays we read a lot of code in Github & friends so we shouldn't rely everything to our favorite editor (at least in terms of readability).
:-1: If aesthetics is the sole motivator for this, I don't think it justifies the risk of introducing yet-to-be-discovered breaking changes.
I'm only a lightweight F# user, but I find 'fun' a small, verbose niggle.
When I switch between F# and C# I always use the wrong arrow for a little while, so for '.net' consistency I would like '=>'.
Given how much F# has inspired C#, I think it's only polite for F# to return the favour ;-)
As a C# dev who's interested in F# I've always found the fun keyword unnecessary and offer very little value.
The fun keyword does seem verbose when moving between C#, F#, and Scala, it would be nice if it could be optional somehow
(\x -> x + 1)
Well better tooling support could fix the shorthand property access. Also, Fun definitely increases readability so I'm not really convinced that symbols increase readability. Even in the so famous python one uses lambda args: ...
So one can live very well with fun and it's arrow.
Other than the case provided by @cloudRoutine , is there any reason we can't just make fun
optional? I do most of my work in C# still and it is bizarre to me that it is more succinct to write lambdas in C# than in F# when F# is supposed to be the more functional language. I love F# and wish I could do all of my work in it, but I must admit that the requirement of fun
is odd.
I think the argument that fun
makes it more clear is weak because there's rarely confusion in C# when a lambda is being used in a LINQ expression. Granted, I have not surveyed all C# developers out there but I haven't run across anyone that got confused by the syntax because there wasn't enough indication of what was going on.
If fun
can't be dropped for parsing reasons, I vote for the use of =>
as the operator to signify I am writing a lambda. While it may be ideal to have only one way to express something in a language, I think a pragmatic approach to design should be considered.
F# already admits to not being a pure functional language. From the outset it has been a balance of functional first but willing to bend when the situation calls for it. I believe this is a case where it would serve F# well to adopt a good design choice from another language. C# has been borrowing from F# for years. Maybe it's time we admit that the C# implementation is easier on the developer and is just as clear?
Is it so bad to make fun
optional? I don't think anyone is saying make it forbidden or syntactically wrong to write (fun x -> x + 1)
. Why not be able to say (x -> x + 1)
? Is that really much less clear? I am asking for my own sake because I wan't to understand if there is a design implication that I am not aware of. Thank you for any feedback. I honestly just want to understand.
I must admit that being mostly C# developer but loving F# I keep forgetting to put 'fun' in lambdas.
What about Kotlin's it
for one parameter lambdas? Has it any problems regarding type inference?
myList
|> List.map (it + 1)
|> List.filter (it < 5)
instead of
myList
|> List.map (fun x -> x + 1)
|> List.filter (fun x -> x < 5)
May be it is even enough to just cover single parameter lambdas, or at least as a first step.
I'm also not convinced with =>
. Please don't take any "pragmatic" ways and introduce some different operators or syntax for the same things. Imagine you should explain later to every newbie about some "historical reason". Let's keep ->
for functions.
@vilinski There's some discussion about using it
in this context here: https://github.com/fsharp/fslang-suggestions/issues/506#issuecomment-346673643
So, I'm going to recant on this one. As I've written more I see the value in having the fun
keyword to provide clarity. While I do like the idea of being able to write (x y => amazingFunc x y)
, it's not that much different than (fun x y -> amazingFunc x y)
. Yes, there are a few more keystrokes but I think the additional clarity is worth it. As stated by others above, I'm also against creating multiple solutions to the same problem. I would hate for the F# community to diverge in style due to some people preferring (fun x ->)
over (x =>)
.
I'm okay with new syntax if there is currently no way to express something clearly and/or concisely (such as suggestion #506). That suggestion I could see being very useful but this one I think would end up causing more pain than it would be worth due to diverging style in the community. As a newbie, it would be terribly confusing to see two different styles for declaring lambdas.
@matthewcrews verbosity adds up since you often have lots of lambdas around the place. Nobody would use fun x -> ... if x => ... became available
@vivainio a fair statement. I agree that verbosity adds up but I believe there should be a balance. Let's say I have a List.
Here's what we have to do now
myList |> List.map (fun x y -> awesomeFunc x y)
Here is is with =>
:
myList |> List.map (x y => awesomeFunc x y)
Sure, using =>
is a little more terse. My question then is, "How far do we go? How far to the side of terseness is ideal?" My concern is that we go too far and up with with syntax like J or K. They are about the most terse languages I've come across.
But I agree, verbosity adds up. We don't want F# to become overly verbose and end up like Java (cheap shot). I guess I'm curious about where that balance is. If F# could make the jump to having =>
be the standard lambda operator, I would be for it provided it would be clear what was going on.
To alleviate a made a ligature,
https://www.dropbox.com/s/cqjp7rdr9fegu69/wlz-code.ttf?dl=0
Ligatures for: (fun (function ()-> ()=>
At this point, personally I'm happy with a ligature as a solution, lol.
TBH my thinking here is that I would prefer a solution along these lines - just make "fun" optional https://github.com/fsharp/fslang-suggestions/issues/168#issuecomment-347926929.
The real question to me is whether this can be implemented cleanly
λ(x y -> x + y)
I like it
The real question to me is whether this can be implemented cleanly
I guess the first thing in making "fun" optional is whether the presence of the "fun" keyword ever disambiguates the arrow "->" in the grammar. But since I have only started into using F# in the past two months, this is difficult for me to judge. Otherwise I might give it a shot.
So... how 'bout this change. It's 2020 and everyone in OO land has a cleaner syntax and has had one for a while now. Just making the 'fun' keyword optional seems pretty straight forward on the surface. It's been said many times but some thing like this looks pretty clean:
[1,2,3] |> List.map(num->num*num) |> printf "%A"
Come on F#.... You know you want it
So... how 'bout this change. It's 2020 and everyone in OO land has a cleaner syntax and has had one for a while now.
It's probably a matter of taste, but I find the syntax of F# in general way cleaner than anything else I've programmed in (Java, C#, C++, Ruby, Perl, Python). To each its merit, but I find F# having a good balance between concise and clear (though it does take a little getting used to).
I'm personally ambivalent about dropping fun
. I probably wouldn't use it, as seeing that keyword in my code helps with readability. And when I don't see it, it helps in telling me that I'm looking at either a value or a curried function.
Come on F#.... You know you want it
Maybe. I don't think there's consensus.
The real question to me is whether this can be implemented cleanly
@dsyme, I don't think it's trivial:
let f _ = 42
[1; 2]
|> List.map (fun f -> f + 1)
Now drop fun
. What is f
? This is even more relevant if we drop the arrow as well and allow implicit variables.
Perhaps there's enough info in the scoping rules in general, but the arguments are patterns, I think there'll be quite some possible ambiguities to work out the scoping, and more look ahead in the parser may be needed.
I'm not saying it's impossible, but if possible, but it appears to me to be an awful lot of work to drop three keystrokes.
Now drop fun. What is f? This is even more relevant if we drop the arrow as well and allow implicit variables.
Not-dropping the arrow could be enough to recognize the pattern on the left, though, it would likely make some places considerably more complex in the parser.
Rust does well by placing the param within pipes. Which would look like:
List.map |f| f + 1
Rust does well by placing the param within pipes. Which would look like:
Interesting suggestion, it also makes the meaning clear(er) as opposed to just dropping fun
. Wonder how it would fair wrt the currently overridable |
operator.
Where I work, we do a lot of mixed C# and F# solutions.
After having now trained and on-boarded several developers of C# background into our codebase, I'm of the opinion that if there's no good reason to not have a similar syntax to c#, we probably should.
It'd be really nice if we could move this non- breaking change forward. It'd make our daily lives better, less typing and less overhead when switching languages.
I think it will also help with adoption, as others have said.
Looking at the other suggestions, I haven't seen any other proposals that hit all these points.
Given the popularity and age of this proposal, are there next steps to moving this forward? Reading the Readme, it sounds like possibly an example implementation my help? I wouldn't mind trying my hand at it, but not if there's some other blocker preventing this from going forward that I haven't seen. Any thoughts on this last bit?
In general I think it'd be nice to make fun
optional, but any design will also have to lambda declarations outside of the "lambda expression when processing lists of data" use case.
Consider the following handler function that you'd find in Giraffe:
let personHandler =
fun (next : HttpFunc) (ctx : HttpContext) ->
task {
let! person = ctx.BindModelAsync<Person>()
return! json person next ctx
}
Does the example look like this now?
let personHandler =
(next : HttpFunc) (ctx : HttpContext) ->
task {
let! person = ctx.BindModelAsync<Person>()
return! json person next ctx
}
Why or why not?
From an orthogonality standpoint, the answer would probably have to be "yes". The following refactoring is possible:
// From here:
let squares = xs |> List.map (fun x -> x * x)
// To here:
let square = fun x -> x * x
let squares xs |> List.map square
// and of course here:
let square x = x * x
let squares xs |> List.map square
It's crucial today that each of these steps compiles and runs exactly the same as it did before.
But what about app models like Giraffe where the lambdas aren't a part of a refactoring progression, but are the end state?
@cartermp I think I'm understanding your point, I'll try to paraphrase and you can let me know.
My thought initially was we would parse the input text and represent both fun x -> x
and x => x
outputting the same AST (I'm pretty new to this compiler, so this may not describe it correctly).
After parsing, the two lambda definitions would be in every way identical.
I think your concern would come up if we parsed them as separate constructs.
I can't really see why we may want do do that, unless it's a requirement for a code formatting feature of the compiler.
Is this what you were driving at?
I'm not talking from an implementation standpoint - the implementation is relevant but not the point. The question is: should fun
be optional in all contexts it can be used?
IMO this should be closed as "won't do"
Valuable time, including commenting on this, better spent elsewhere.
Gotcha. I think it stands to reason that the alternative syntax should be valid in all contexts. What would the argument be for not allowing the alternative syntax in the giraffe example? (Not from an implementation point of view)?
let personHandler =
(next : HttpFunc) (ctx : HttpContext) =>
task {
let! person = ctx.BindModelAsync<Person>()
return! json person next ctx
}
Why could this be considered bad?
I think it may be valuable to nitpick the syntax here though.
For example should extra parenthesis be required, as would be familiar in other languages that only accept tupled arguments.
Eg:
( (next : HttpFunc) (ctx : HttpContext) ) =>
vs
(next : HttpFunc) (ctx : HttpContext) =>
I guess the alternative syntax is still an open question. Should it just be optional fun
, or should we use the C# "fat arrow" syntax, which I was in favor of a few posts up and was suggested originally as one of the first comments in this issue. (I'm rudely discounting other suggestions under the rational that they won't be familiar to C# programmers or will not be quick to type. This is my personal bias and I'm doing it for convenience to try to move this issue forward by eliminating options. Please call me out if we're not at this stage with this suggestion)
*Now that I re-read my post, it wasn't clear at all that I was proposing x => x
syntax.
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