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
proposal: Go 2: Partially Applied Functions #29171
Comments
This is often call currying. |
@ianlancetaylor Not really. Currying is translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument. Something like this: func f(a int)(b int) int {
return a + b
}
func client() int {
return f(1)(2)
} In most of the FP languages, it is not mandatory to use currying for applying partial function. It can be done using a blank operator (as proposed in the issue). Yet, it is often a mixed concept because it may seem more natural to partially apply a currying function to prevent having to use a blank operator. Something like this: func f(a int)(b int) int {
return a + b
}
func client() int {
pf := f(1) // pf is a func(int) int
pf(2)
} |
Just for clarity, would this be (similar) to Function.bind in JavaScript? And if it is, I think the syntax is a bit misleading. To me it looks like we are calling |
@deanveloper I'm not familiar with Javascript, to be honest, but I don't really think it is similar. Here, this is just a mechanism to create a function from another function. The alternative syntax is what we discussed above with currying function. But this is for me another topic (which may or may not be interesting for Go v2, I don't know). |
You can already do this, with a bit of boilerplate: package main
import "fmt"
type Context struct {
blue bool
}
func (c Context) isBlue() bool {
return c.blue
}
func bar(context Context, i int) int {
if context.isBlue() {
return i + 1
} else {
return i - 1
}
}
func apply_bar(apply func(Context, int) int, context Context) func(int) int {
return func(i int) int {
return apply(context, i)
}
}
func foo(f func(int) int) {
fmt.Printf("Calling with 1: %d\n", f(1))
}
func main() {
context := Context{false}
f := apply_bar(bar, context)
foo(f) // Call foo with f
context.blue=true // see what happens if context changes
foo(f) // Call foo with f, stays the same since context was copied by value.
context2 := Context{true}
f2:= apply_bar(bar, context2)
foo(f2) // Call foo with f2
} I'd say, use go generate or write specific library to solve this problem. |
@beoran You're right it works but we still have to write something specific for that. It's still far more elegant, in my opinion, to manage partial functions. Don't you think? Do you mind if I quote your solution as an alternative with the current Go version? |
Maybe I'm missing something but it seems this proposal is just syntactic sugar for a closure:
You allude to closures but the example is maybe a bit contrived: both partial application and a closure are one-liners. From the book The Go Programming Language:
This appears to me to be a pernicious change (in terms of legibility) for the sake of convenience. |
@lrewega @beoran Thanks for your remarks. Yet, what about the following example? i := 5
f := func() int {
return i
}
i = 6
fmt.Printf("%v\n", f()) Here, it displays |
I wish Go had partial functions, but partial functions are mostly useful because of currying. Using |
We shouldn't implement immutability into only one feature of the language. That's just confusing and isn't consistent with the rest of Go. |
@deanveloper I'm not sure to get your remark. For example, a Here, I was not proposing a deep change with immutable data structures or whatever. Just a way to fix a function argument ;) |
That was my bad, I didn't mean immutability in that sense. I mainly just meant that an out-of-scope variable always holds the value that it holds and never gets "fixed" to anything in Go, and I think it would be a bit better (and more intuitive to read) if you save the value to a variable, use an anonymous function, and call that instead. For instance with your example: i := 5
iInF := i
f := func() int {
return iInF
}
i = 6
fmt.Printf("%v\n", f()) Would print 5 |
@deanveloper Sure but how do you write a unit test? You need to wrap it in another function. Maybe something like this: #29171 (comment) Obviously, there is no a hard limitation. It's just an elegant solution (in my opinion) to solve a common problem without having to write custom code. |
Another option with go2 generics:
|
I updated my example boved and linked correctly to go playground to show that if you pass the context by value, so it gets copied, you get the effect of partial function application without interference from captured variables. True, there is some boilerplate to write (as always in Go), but as @reusee shows, generics should solve that problem once we get them. So I'd rather push for generics that for this rather limited use feature. |
@beoran Why not both? :) |
@teivah Go has been from the "get go" a programming language that only includes the most widely useful features.This to avoid the language from becoming too complex and too hard to understand. If we now look at https://blog.golang.org/go2-here-we-come for the Proposal Selection Criteria, namely:
Then I think the feature proposed in this issue does not meet 1. It is not an important issue for many people. Of course I am only a sample of one, but I miss enums in Go, I miss unions, and I sometimes miss generics. But I never missed partial functions, especially because Go already has closures that allow you to emulate them relatively easily. That's why I respectfully ask that this proposal be declined. |
@beoran Ok I do understand. No problem. |
@teivah I'm sorry, but you're not using the terms correctly. The term partial function refers to a function that doesn't return for all possible arguments, but crashes or doesn't terminate for some. The opposite of a partial function is a total function, a function that returns a value whatever argument you provide. Even the link you provided explains this, so it seems like you haven't even read it. The term you're looking for is partial application or currying, which is taking a function and filling some of its arguments and thereby getting a function that takes the remaining arguments. |
@faiface Right, maybe I should have rather used the notion of partially applied function. Yet, I disagree currying is not the same thing in my opinion (see my comment there: #29171 (comment)). |
@teivah Yeah, no problem, just wanted to clarify what means what :). Other than that, I think partial applications could be useful in Go, although not very often I believe. |
This proposes a new feature to the language that can be done using other mechanisms, and that few people are asking for. Also, the syntax does not seem intuitive to me. If I can write If we want to make changes in this area it's perhaps best to consider #21498. |
This feature would be super useful for some data analysis tasks. For example (basic curve fitting): I have some function However, let's say I have data for which there is some fixed value of |
You can already do that, of course, using a function literal. This proposal is simply suggesting a more concise mechanism. In any case, this proposal is closed. Personally I think that #21498, which explores more general mechanisms, is likely to be a better path forward. Though there is no guarantee that that proposal will be accepted either. |
@ianlancetaylor I think #21498 solves a completely different problem. |
Let's consider a higher-order function
foo
taking as an argument another function:Let's now imagine we need to call
foo
by passing a function that would depend on an external context (something not passed as arguments) like aContext
interface:Today's Option 1: Custom Structure and Method
We wrap this
Context
in a custom structure and we pass a method tofoo
.For example:
Today's Option 2: Pass a Closure
We create a closure and we call
foo
this way:Proposition: Partially Applied Function
In FP languages, there is a more elegant solution allowing to facilitate the unit tests: partially applied functions.
First, it allows to keep passing a pure function which does not depend on any external context. Then, writing an unit test with a partial function is way easier as it depends only on the input arguments.
Furthermore, the fact that Go does not allow function overloading could really ease the implementation/adoption of partially applied functions in my opinion.
It would be awesome to have such features in Go 2 :)
The text was updated successfully, but these errors were encountered: