Parameter Functions

I've had an idea for a while now, but I am having trouble coming up with a good syntax for it, so I thought I would ask the list if anyone has good ideas on how to spell it.

There are a lot of times where you have an exponential explosion of similar functions with slightly different parameters. Parameter defaults help us to avoid this in the case of optional parameters (i.e. I can write one function that will surface all permutations of optional parameters being used or not), but when you have two related types you still have to write two functions. For example:

func foo(_ a: CGFloat) {...}
func foo(_ a: Double) {self.foo(CGFloat(a))}

With 2 different pairs of related types that vary independently, you need four functions. It only gets worse from there.

Subtype relationships will help here, but there are a lot of times where you would want that relationship only to apply to the parameter, and not to the types as a whole. For example, Swift is opinionated that you should have to be explicit about casting between Double and CGFloat, and we don't want to change that... but it would still be nice in certain cases to easily define API that takes either.

The idea is to provide a type of function for the parameter which maps from one parameter set to the base parameter. Something like:

func foo( _ a: CGFloat) {...}
parameter func foo.a( _ a: Double) -> CGFloat { //This would have to return a's type within foo
    return CGFloat(a)
}

This would then make the following signatures usable:

foo( _ a: CGFloat)
foo( _ a: Double) 

The parameter name wouldn't have to be the same, and it could be replaced by multiple parameters (but it would still have to be in the same position):

func bar(a: Int, pt: CGPoint) {...}
parameter func bar.pt(x: CGFloat, y: CGFloat) -> CGPoint {
     return CGPoint(x: x, y: y)
}

//Allows the following:
bar(a: Int, pt: CGPoint)
bar(a: Int, x: CGFloat, y: CGFloat)

The benefit of this is that you can have multiple parameters varying and you only have to write one (short) function for each variance. No more exponential growth of parameters.

func bar(a: Int, pt: CGPoint) {...}
parameter func bar.pt(x: CGFloat, y: CGFloat) -> CGPoint {
     return CGPoint(x: x, y: y)
}
parameter func bar.a(nearestTo num: Double) -> Int {
    return Int(num.rounded())
}

//Allows the following:
bar(a: Int, pt: CGPoint)
bar(a: Int, x: CGFloat, y: CGFloat)
bar(nearestTo: Double, pt: CGPoint)
bar(nearestTo: Double, x: CGFloat, y: CGFloat)

Ideally you could mark these @compileTime (whatever that ends up being called), so some cases could be computed at compile time.

Thoughts? Do you have a better syntax for this?

1 Like

The easiest way to implement this would be to have the compiler generate functions with the new signature that just call the main function and the parameter function:

func foo( _ a: CGFloat) {...}
parameter func foo.a( _ a: Double) -> CGFloat { //This would have to return a's type within foo
    return CGFloat(a)
}

//generates:
func foo( _ a: Double) {
    foo( foo.a(a) )
}

But I can imagine that their might also be a way to represent it more compactly in the checker to improve performance. i.e. we should do something similar to what parameter defaults do.

My solution would be to allow the use of private protocols in public methods, and have the compiler generate the exposed overloads based on the types which conform.
So, you would write a function which takes MyPrivateFloatingPoint — but for clients, this would appear as a set of methods whose parameters have no visible connection.

2 Likes