fsharp / fslang-suggestions

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

Make the `fun` keyword optional #168

Open baronfel opened 7 years ago

baronfel commented 7 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

ncave commented 7 years ago

+1 to fat arrows, all in the name of driving adoption for the language.

cartermp commented 7 years ago

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)

cloudRoutine commented 7 years ago

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

cartermp commented 7 years ago

Good point, => would make cases like that unambiguous.

gusty commented 7 years ago

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.

charlesroddie commented 7 years ago

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.

Rickasaurus commented 7 years ago

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.

sonhanguyen commented 7 years ago

+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?

wiwiwa commented 7 years ago

+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

matthewcrews commented 7 years ago

+1, if it is possible to make fun optional while still being unambiguous, it would be greatly appreciated.

AnthonyLloyd commented 6 years ago

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.

realvictorprm commented 6 years ago

I don't like to remove the 'fun' from F#. I would rather discuss about an alternative syntax for very short lambda expressions.

MangelMaxime commented 6 years ago

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.

TIHan commented 6 years ago

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.

jichang commented 6 years ago

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

realvictorprm commented 6 years ago

@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!

pchalamet commented 6 years ago

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.

vivainio commented 6 years ago

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}
vilinski commented 6 years ago

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!

AndreuCodina commented 6 years ago

@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 (*)
alfonsogarciacaro commented 6 years ago

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)
let foo (f: 'a->'b, x: int) = x
let y = foo(fun x -> 5, 6) // Doesn't compile
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).

eiriktsarpalis commented 6 years ago

:-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.

codybartfast commented 6 years ago

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 ;-)

connelhooley commented 6 years ago

As a C# dev who's interested in F# I've always found the fun keyword unnecessary and offer very little value.

stevewillcock commented 6 years ago

The fun keyword does seem verbose when moving between C#, F#, and Scala, it would be nice if it could be optional somehow

wannado commented 6 years ago

(\x -> x + 1)

realvictorprm commented 6 years ago

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.

matthewcrews commented 6 years ago

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.

eugbaranov commented 6 years ago

I must admit that being mostly C# developer but loving F# I keep forgetting to put 'fun' in lambdas.

vilinski commented 6 years ago

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.

alfonsogarciacaro commented 6 years ago

@vilinski There's some discussion about using it in this context here: https://github.com/fsharp/fslang-suggestions/issues/506#issuecomment-346673643

matthewcrews commented 6 years ago

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.

vivainio commented 6 years ago

@matthewcrews verbosity adds up since you often have lots of lambdas around the place. Nobody would use fun x -> ... if x => ... became available

matthewcrews commented 6 years ago

@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.

wiltonlazary commented 6 years ago

To alleviate a made a ligature,

https://www.dropbox.com/s/cqjp7rdr9fegu69/wlz-code.ttf?dl=0

Ligatures for: (fun (function ()-> ()=>

https://github.com/tonsky/FiraCode/issues/645

voronoipotato commented 5 years ago

At this point, personally I'm happy with a ligature as a solution, lol.

dsyme commented 5 years ago

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

Oxicode commented 5 years ago

λ(x y -> x + y)

I like it

jonathan-markland commented 5 years ago

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.

rahicks26 commented 4 years ago

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

abelbraaksma commented 4 years ago

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.

auduchinok commented 4 years ago

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.

7sharp9 commented 4 years ago

Rust does well by placing the param within pipes. Which would look like:

List.map |f| f + 1

abelbraaksma commented 4 years ago

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.

lenzenmi commented 3 years ago

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?

cartermp commented 3 years ago

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?

lenzenmi commented 3 years ago

@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?

cartermp commented 3 years ago

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?

jackfoxy commented 3 years ago

IMO this should be closed as "won't do"

Valuable time, including commenting on this, better spent elsewhere.

lenzenmi commented 3 years ago

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.