-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lambda Support #1066
Comments
Hi, Thanks. This was briefly discussed here: The syntax I'm considering is
One thing I'm worried about is that it's very easy easy to write unreadable code with complex nested maps. I'm not sure what is the best way to prevent this. |
Summarizing my comment from that issue: That lambda syntax is not very general, it doesn't allow multiple lambda arguments. |
I believe that a explicit declaration of the lambda but implicit parameter inference could work together: doubled_ints := ints.map(_ * 2) // What does _ mean? Is it a number? A variable? However, a doubled_ints := ints.map(fn _ * 2) //Oh, _ is part of a function Parameters could be added at will: doubled_ints := ints.map(fn (x) x * 2) However, implicit return would aid in increasing readability |
It's often good to have meaningful names for parameters to help the reader, even for lambdas. What if any single underscore prefixed identifiers odd := container.filter(
fn _elem & 1)
sum := container.reduce(
fn _total + _elem)
container.sort(
fn _a < _b) |
doubled_ints := ints.map({ return it * 2 }) like kotlin |
So, considering all your opinions, I think the syntax for lambda expressions should follow the following rules:
Lambdas in V would support Target Typing. fn map(arr int[], func fn (int) int) int[] {
// Do the mapping
} And later: map([1, 2, 3], fn (x /* int type inferred */) /* return type int inferred */ { return x * 2 }) Rule 1. All lambdas are prefixed with Rule 2. Lambdas follow the syntax regulations for normal functions, with the exception that the alphanumeric identifier after Rule 3. Lambdas that take no parameters may omit the repeat(3, fn {
println("Hello World!")
}) Rule 4. Lambdas that consist of only one doubled_ints := ints.map(fn (x) x * 2) Rule 5: Rule 3 and Rule 4 may be used together: list_of_threes := ints.map(fn 3) Rule 6: Marking a valid name with a doubled_ints := ints.map(fn x! * 2)
total := ints.reduce(fn total! + value!) Translation to V (as of now): fn main(){
products := zipWith(fn x! * y!, [1, 2, 3], [4, 5, 6]) // ==> [4, 10, 18]
} At compile time it would become: fn lambda_1 /*unique name generated by compiler*/ (x int, y int) int {
return x * y;
}
fn main(){
products := zipWith(lambda_1, [1, 2, 3], [4, 5, 6]) // ==> [4, 10, 18]
} Tell me what you think of this "proposal" for V lambda expressions. |
V is a simple language. You just introduced 6 extra rules that have to be learned. Rule 3 means that there are now 2 different ways to write lambdas without parameters. One of V's main philosophies is to have only one way of doing things. So ideally, no new syntax would have to be introduced at all, just like in Go:
map, filter, etc often have one liners, so I propose the
@ntrel It won't be possible to declare a variable named For multiple arguments the traditional syntax will be used with full types specified. I think omitting the type is only readable when there's only one argument, the function is simple, and it's clear what the type is, like in the |
I agree syntax rules should be simple. This cuts out the return keyword and the noise of the braces, but still supports naming parameters and more than one parameter. It is also much closer to anonymous function syntax than just
|
|
@medvednikov makes a convincing argument. There should only be one way of doing things. V should support support lambdas that look just like functions without names. However, I still feel like the |
@medvednikov how would you feel about something like doubled_ints := ints.map(& * 2) It also has a familiarity to it for anyone that's used Ruby or SASS (maybe others too, idk). |
Why do not just use the same syntax that it is used in named fn?. Using any hidden trick like _, & or 'it' adds confusion to the reader, limits parameters to 1 and does not support nested lambdas. You could try to remove {} as @N8python suggested to make it "cleaner" when it is a one liner:
One thing to discuss should be if the return type is mandatory or always infered. |
I agree with @aguspiza that the best syntax for one liners would be |
@aguspiza the point behind the shorter syntax is just that, it's shorter and more concise for simple operations. I know in Ruby I use it all the time for I know some people aren't fans of syntactic sugar like that, but it does make things a bit more concise. Of course we should still have a full lambda syntax, and for that I agree that the |
It's used in many popular languages with functional features like Scala, Swift, Ruby, Kotlin. I wouldn't call it confusing.
So now we have 3 lambda syntaxes :) |
@medvednikov 3? What's the third? I only see the short syntax and the long syntax |
|
I feel like lambda syntax is very complicated as it is implemented in so many different ways. Ie. Javascript has 6 different ways of writing the same line of code for doubling all ints in an array: ints.map(x => x * 2);
ints.map((x) => x * 2);
ints.map(x => { return x * 2 });
ints.map((x) => { return x * 2 });
ints.map(function(x){return x * 2 });
ints.map(function doubler(x){ return x * 2 }); Don't even get me started on Scala. V, being revolutionary in its one-way-only idea (lots of languages CLAIM to have "one way" to do something, but they have 3 or 4), should perhaps only have ONE lambda syntax: the original |
I think it's a very good option, if you mean only having More typing, but having only one way fits really well with V's philosophy. |
I like : ints.map(fn (x int) { return x * 2 }) Can you declare "local" functions like:
Of course inline code ;-) |
I think you would declare inline functions as: check := fn (x int) bool {
return true
} |
I think we are close to reaching a sort-of consensus. Right now, I agree with @medvednikov that V should only have one way to do lambdas, even if it means extra typing. Explicit typing in lambdas may be cumbersome, but, as @medvednikov said, lambdas should be close to regular functions. |
If we're going to go that route, why not just make functions first class citizens? Then not only could you create a lambda by assigning a function to a variable, but you could also pass other non-lambda functions around. Thoughts? |
I agree. I think V would benefit greatly from first-class functions. The only thing I'm not sure about is closure. V needs to be fast, and closure could slow it down. @medvednikov, what would you think about making functions first-class citizens but not implementing closure? |
I don't think that is a lambda, just an anonymous function. A lambda does not have statements. |
Closures are fast if they don't escape the scope they are declared in, then they don't need to be heap allocated. GNU C supports these closures. |
So... you think that V should allow first-class functions and closure, but not allow "heap closure", where the variables escape the function? That sounds reasonable. |
I agree. First class functions would be an amazing addition, but closures should definitely be a thing. Not allowing "heap closures" would be a good compromise to keep performance up. |
So, our consensus is to allow anonymous functions, first-class functions, and closure, but not "heap closure". Please point out if this is not a satisfactory resolution. |
Shouldn't this wait to be closed until the feature is actually added? |
Ok, I’ll reopen it.
… On Jul 16, 2019, at 5:21 PM, Chris Watson ***@***.***> wrote:
Shouldn't this wait to be closed until the feature is actually added?
—
You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub <#1066?email_source=notifications&email_token=AKKMMKEB5V2UNXJ7SKBZ4MTP7Y3VVA5CNFSM4H7I6LTKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2CGETY#issuecomment-511992399>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AKKMMKAFIN3BBLKCFWD3K5DP7Y3VVANCNFSM4H7I6LTA>.
|
Having closure syntax as anonymous functions is fine and is very inline with the "one-way" philosophy. But there are two different places where they are used, and I would like to have different approach towards argument types declaration: one thing when you declare the function taking function or closure as argument, another one is when you use it: fn (a mut []int) map(func fn (x int) int) { ... } but [1, 2, 3].map(fn (x) x * 2) I would like to have it this way. |
Actually ints.map((x) => { return x * 2 } syntax let you write any lambda in JS. Implementing was NOT very hard so they create additional options to do so (some archaic ones are with 'function' keyword). In my opinion lambda syntax SHOULD be different because it lets you find where lambda function is very quickly. If you will have just fn keyword and nest several functions in one another it will be hard to check what's going on. Here you have two factors: no name and arrow syntax. Look at Pony, Haskell, Coconut etc. |
I understand. However, I dislike the arrow syntax if you already have the ints.map(fn (x) x * 2) would be a good compromise. |
@medvednikov I think this is the best way to go, anonymous (lambda) function definition shall be exactly like a standard definition, but without the need of a name I also encourage that functions in general require at least one "return" statement, there should be no implicit return values allowed, which makes it more readable especially when there is nothing to return like "none". |
You could find them all with a regexp, although probably you'd be looking for a specific usage instead and search for e.g.
This seems fine: strings.filter(
fn (s) s.find(
fn (c) c.is_alpha()) != none) Given that if and match expressions implicitly yield a result, it seems a bit odd not to have that for lambdas, which are meant to be short (otherwise they're just function literals). Even requiring braces without return would be better than requiring return. |
How about the "it" keyword? Like in Kotlin lambdas: Short lambda
Long lambda
Typed short lambda
Typed long lambda
"Brackets" short lambda
"Brackets" long lambda
Lambda with receiver
|
Short optimized filter/map have been implemented:
For everything else the same syntax ( |
That's awesome! I like the balance between implicit and explicit - shorthand syntax for common cases, and more verbose syntax for the uncommon ones. This is a great resolution. |
That was exactly the reasoning :) Glad you like it. |
I think the real issue is type inference, which is very powerful, but not easy to implement. The syntax |
Return type inference is very easy to implement and would not slow down the compiler: fn (x int) {x * 2} I would limit it to a single expression.
map, filter and sort (each only on arrays) does not cover all the common cases. |
Still hoping for a syntax where the callback's typing is infered from the initial definition. Having only a few methods sprinkled throughout with the Hoping this will bepicked back up eventually and improved on :) |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
V is a concise and elegant language, and I think a syntax for lambda expressions could go along with the spirit of V. A lambda could be declared just like a function but without a name:
However, lambdas would have some additional convinces compared to regular functions. Target Typing would allow the lambda to drop the types on it's parameters.
Furthermore, if the lambda consists of a single
return
statement, the braces may be emitted and the return will be implicit:If the lambda doesn't take any parameters, the parens after
fn
may be omitted:The implicit return and parameterless
fn
could be combined:Closure could be implemented once V is more mature. For now, the ability to create anonymous functions would be a huge help to V programmers seeking a more functional style.
The text was updated successfully, but these errors were encountered: