golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
122.67k stars 17.49k forks source link

proposal: Go 2: currying functions with `f{x}` and `f{arg: x}` #66184

Closed ezz-no closed 4 months ago

ezz-no commented 6 months ago

Go Programming Experience

Intermediate

Other Languages Experience

No response

Related Idea

Has this idea, or one like it, been proposed before?

No

Does this affect error handling?

No

Is this about generics?

No

Proposal

Allow to construct function composite literal, inside or outside function, to provide a convenient way of constructing named function out of existing function in the case of the only difference is that some arguments differ

Language Spec Changes

Treat function name as type in the case of composite literal

Informal Change

Example:

package main

func f(a int, b int) int {
    return a + b
}

func main() {
    v := 1

    f0 := f{v}
    /*equivalent to
        f0 := func(b int) int {
        f(v, b)
    }*/

    f1 := f{1, 6}
    /* equivalent to
        f1 := func() int {
        return f(1, 6)
    }*/

    f2 := f{b: 3}
    /* equivalent to
        f1 := func(a int) int {
        return f(a, 6)
    }*/

    assertEqual(f(1, 10), f0(10))
    assertEqual(f(1, 6), f1())
        assertEqual(f(2, 3), f2(2))
}

Is this change backward compatible?

Yes

Orthogonality: How does this change interact or overlap with existing features?

No

Would this change make Go easier or harder to learn, and why?

Could understand easily, the concept is well known

Cost Description

Complicate the difficulty of distinguishing function as value or type

Changes to Go ToolChain

Not familiar

Performance Costs

No

Prototype

Types:treat function as type in the case of composite literal Noder/writer:write down base function object info and fixed arguments Noder/reader:construct the closure accordingly

Jorropo commented 6 months ago

This proposal break the assumption that arguments names are not part of the API. reflect.Type.In only ever use indexes, in general within the rules of the language there are only positional arguments as far as signatures goes.

It's a backward compatible change, but I don't know if we want something like that.

As an example of why adding meaning to arguments of function looks weird to me: I think it would be weird if we didn't applied public / private rules on the argument names too. So:

otherPackage.Foo{b: 6}

shouldn't be allowed imo, for consistency with everything else that use named keys.

I think it's equally weird if we apply public / private rules to argument because what would this do:

// otherpackage.go
func Foo1(B int) {}
func Foo2(b int) {}

// package.go
var a func(b int) = otherPackage.Foo1 // this shouldn't probably fail
var b = a{b: 52}
var c func(B int) = otherPackage.Foo2 // should this fail ? it doesn't currently, and would be breaking change, but it takes a private argument and make it public
var d = c{B: 52} // should this fail ? should the `c =` assignement remember if the original function had public or private arguments, but that a runtime fail

We would also need a syntax like python's /, * to indicate if arguments can be keyed or positional.


Adding argument names to the signature of the function looks like a big can of worms to me, and the value of your proposal is very limited (some syntax sugar), I think this proposal shouldn't be accepted without a more complete story for named arguments as part of the api (like foo(x, y, compare: func(x, y int) int { return cmp(abs(x), abs(y)) }))

leaxoy commented 6 months ago

It's called currying, and it's a very important concept in functional programming, but I don't think the Go team would choose to introduce them.

seankhliao commented 6 months ago

previously #4633 was closed as a dup of #21498

DeedleFake commented 6 months ago

In Elixir, there's a 'capture operator', &, which lets you turn any expression into a function. It looks like &Example.function("example", &1). This returns a function that takes a single argument and, when called, evaluates that expression, passing in its single argument in place of &1. More arguments can be given, i.e. &2, &3, etc. At least one must be given. Other expressions are allowed besides function calls, too, such as &(&1 + "example"), but my guess is that that could be complicated in Go as it's statically typed.

I don't think it would make sense to overload the & operator in Go, but something else could be used, such as $. I think that's what Swift does.

I'm not necessarily advocating this, but this approach might be cleaner. It separates it from the argument names this way.

ianlancetaylor commented 6 months ago

This amounts to syntactic sugar, since you can already use a function literal. f{v} is equivalent to func(b int) int { return f(v, b) }. So an important question is: how often would real code use this feature? Is it common enough to be worth introducing a new syntax? Especially bearing in mind that the new syntax is somewhat obscure. The difference between using { and ( is subtle.

ianlancetaylor commented 5 months ago

Based on the discussion above, and the emoji voting, this is a likely decline. Leaving open for four weeks for final comments.

ianlancetaylor commented 4 months ago

No further comments.