Partial application using a reserved character

Hello everyone!

I would like to propose adding partial application to Swift, allowing developers to create new functions by pre-filling some arguments of an existing function. I know that currying was removed, but I think we can consider this feature independently, as it brings a powerful addition to the Swift language.

This can enhance code reusability, and streamline function handling. The proposed syntax involves using a reserved character $, but it could be another character or in another way for example&. Let me explain with this function:

func foo(foo1: String, foo2: Int) -> [String]

let partialFoo = foo$(foo2: 5)
// Returns a function of type (String) -> [String]

But for functions without parameter names, partial application can be applied using positional placeholders:

var foo: (String, Int) -> [String]
let partialFoo = foo$($1: 5)
// Returns a function of type (String) -> [String]

I think that the use cases of this feature could be a lot giving to Swift a new posible approach for the functional programing.

Another posible case could be if you send all the parameters with the partial application, in that case you would obtain a () -> T function like this

let partialFoo = foo$("foo", 5)
// Returns a function of type () -> [String]

That would be useful for example in SwiftUI where you can send the parameters but as you get the void function you can send it to the action with only one more character and avoiding adding a new closure to execute it:

func foo(foo1: String, foo2: Int) -> Void

Button("Some Text", action: foo$("foo", 5))
// Returns a function of type () -> Void and is sent to the action

Thank you, everybody, for the incredible work that you have been doing over the past 10 years!

Update
Another alternative way could be with a reserved word like partial, for example:

let partialFoo = partial foo("foo", 5)
// Returns a function of type () -> [String]

Thanks to @crontab for the suggestion

4 Likes

This will be a huge step forward, unlike some recently accepted feature.

To emulate the partial application, I attempted to write a function adaptor:

struct FunctionAdaptor <each Arg, Result> {
    let f: (repeat each Arg) -> Result
    
    func callAsFunction (_ u: repeat each Arg) -> Result {
        f (repeat each u)
    }
}

But, I instantly hit a brick wall: how to pre-fill arguments. :slight_smile:

Failed Attempt
@main
enum Temp {
    static func main () async throws {
        do {
            func f () -> Void {
                print (#function)
            }
            
            let g = FunctionAdaptor (f: f)
            g ()
        }
        
        do {
            func f (_ u: Int) -> Int {
                print (#function, u)
                return 3 * u
            }
            
            let g = FunctionAdaptor (f: f)
            print (g (2))
        }
        
        do {
            func f (_ u: Int, _ v: Int) -> Int {
                print (#function, u)
                return (u - v) * (u + v)
            }
            
            let g = FunctionAdaptor (f: f)
            print (g (3, 5))
            print (g (5, 3))
        }
    }
}

struct FunctionAdaptor <each Arg, Result> {
    let f: (repeat each Arg) -> Result
    
    func callAsFunction (_ u: repeat each Arg) -> Result {
        f (repeat each u)
    }
}

Purely for aesthetical and readability reasons, how about:

let partialFoo = func foo(foo2: 5)

func as an expression is not used anywhere in the language afaik, and looks a bit better than the $ to me.

Edit: no, probably a bad idea, sorry :frowning:

Maybe another word like partial?

let partialFoo = partial foo(foo2: 5)
2 Likes

I liked the originality of use the parameter packs :grin:, I was thinking in doing a mock with Macros but I think that the positional placeholders would not be posible.

How would the partial-application syntax look like for member functions?

Essentially this idea (with a slightly different syntax) was discussed as part of the SE-0021 review process and the core team’s evaluation was as follows:

12 Likes

Please correct me if I'm wrong about anything. As far as I understand and remember, it was decided to do away with currying in Swift 3 because the current syntax was considered a better option. If it came down to choosing between currying and a simpler, less ambiguous syntax for calling functions, the latter was preferred.

I must say I fully agree with this idea and believe that the changes made were very positive for the language. However, I think we can find alternative ways to incorporate partial functions without losing the characteristics that the language has now.

I believe the main advantage of the syntax I propose is that it allows for partial application without changing the way functions are called. This debate was closed at the time because a solution was chosen, but it would be beneficial to explore alternative ways to incorporate certain functional programming features into the language. This is a topic that was dismissed many years ago, and the language today is completely different. Revisiting these topics might lead to original ideas that align better with the current philosophy, now that Swift is much more established.

Thank you all very much.


@ibex10 As I understand I think that the syntax would be the same no? But, for example, I have doubts about how a weak self would affect to this kind of function call.

1 Like

Past rejections don’t expire, and the language is certainly not “completely different.”

When a past decision rests on premises that are no longer applicable, or when there are concrete identifiable developments that alter the calculus, then we may certainly revisit a topic. If you can identify some specific aspect of the core team’s reasoning here that clearly fits that description then, sure, but I cannot.

3 Likes

I am not agree but I understand your point.

Even though I would like to encourage to the team to discuss about it. I think that my idea is different than the idea descarted before or it could lead to other ideas.

Thanks!