Partial application is a technique of applying a function to some of its arguments. As a result of the partial application we get a function of the remaining arguments.
But before we proceed with a partial function application, what does a plain Application mean?
Application is a process of applying a function to its arguments to get a result.
When we say that a function is applied to its arguments, we usually mean Full application - when we provide all arguments required by the function signature.
For example, a function Apply2
below does a full application of a 2-argument function f
to 2 provided arguments a
and b
to produce a result of type C
:
func Apply2[A, B, C any](f func(A, B) C, a A, b B) C {
return f(a, b)
}
In contrast to Full application, Partial application means that we would not provide all arguments for the application, but only some of them (in case of a 2-argument function, only 1 of the 2 arguments).
Go doesn't support a partial function application out of the box, but it's possible to implement it by hand. For example, helper functions for a partial application of 2-argument functions could look like that:
func Apply2Partial_1[A, B, C any](f func(A, B) C, a A) func(B) C {
return func(b B) C {
return f(a, b)
}
}
func Apply2Partial_2[A, B, C any](f func(A, B) C, b B) func(A) C {
return func(a A) C {
return f(a, b)
}
}
The first function partially applies a 2-argument function f
to its 1st argument of type A
and returns a function of 1 remaining argument of type B
. The second function partially applies a 2-argument function f
to its 2nd argument of type B and returns a function of 1 remaining argument of type A.
The reason of having these (slightly inelegant) function names is because Go doesn't support Function/Method overloading. In other words, it's not possible to define 2 functions with the same name in the same package, even if functions take different arguments as parameters. It's little bit unfortunate but we can live without that.
By extension, similar helper functions for a partial application could be implemented for 3-argument functions:
func Apply3Partial_1[A, B, C, D any](f func(A, B, C) D, a A) func(B, C) D {
return func(b B, c C) D {
return f(a, b, c)
}
}
//...
func Apply3Partial_2[A, B, C, D any](f func(A, B, C) D, b B) func(A, C) D
func Apply3Partial_3[A, B, C, D any](f func(A, B, C) D, c C) func(A, B) D
func Apply3Partial_1_2[A, B, C, D any](f func(A, B, C) D, a A, b B) func(C) D
func Apply3Partial_1_3[A, B, C, D any](f func(A, B, C) D, a A, c C) func(B) D
func Apply3Partial_2_3[A, B, C, D any](f func(A, B, C) D, b B, c C) func(A) D
And then we can use these helper functions to do an actual partial application of our functions, for example:
f := func(a int, b bool, c float64) string {
return fmt.Sprint(a) + " " + fmt.Sprint(b) + " " + fmt.Sprint(c)
}
// function `f` is applied only to the 1st and the 2nd argument.
// resulting function `p` has only 1 remaining argument.
p := Apply3Partial_1_2(f, 10, true)
fmt.Println(p(5.5))
// Output: 10 true 5.5
Conclusion
In this article we explored Partial application of functions and how it could be implemented in Go. If you are interested to deep dive into the topic of functional programming, you are welcomed to join me on GitHub where I develop a library for functional programming in Go: github.com/ialekseev/go4fun. We can play together :) Thanks for reading!