About Swift functions, named parameters, and type management - swift

Suppose I have a function overloaded as such:
func doMath(mathOption: String) -> (Int...) -> Double {
...
return average
}
func doMath(mathOption: String) -> ([Int]) -> Double {
...
return average
}
Side note: Function average itself is overloaded to accept both an array as an input or a list of parameters.
Two questions:
1 - How do I reference which function I am referring to?
For example:
let doAverage = doMath(mathOption: "average")
How do I specify which doMath function I'm calling? Swift is confused and can't infer from the next line:
If I later write:
doAverage(1,2,3,4)
2 - How do I name parameters? The original average function is called thus:
average(nums: 1,2,3,4)
I have to name the parameters. Yet with doAverage, I can't name parameters because of how the return type is defined.
3 - How could I create a type (perhaps using struct?) to simplify this hypothetical code.
Thanks for any help, explanation, or answers you offer!
Edit, to clarify 3, here is the expanded version of the situation:
func sumAll(nums: [Int]) -> Double {
return Double(nums.reduce(0, { (a,b) in a+b}))
}
func sumAll(nums: Int...) -> Double {
return sumAll(nums: nums)
}
func average(nums: [Int]) -> Double {
return sumAll(nums: nums) / Double(nums.count)
}
func average(nums: Int...) -> Double {
return average(nums: nums)
}
func doMath(mathOption: String, nums: Int...) -> Double {
if mathOption == "average" {
return average(nums: nums)
} else {
return sumAll(nums: nums)
}
}
typealias mathReturnType1 = (Int...) -> Double
typealias mathReturnType2 = ([Int]) -> Double
func doMath(mathOption: String) -> mathReturnType1 {
return average
}
func doMath(mathOption: String) -> mathReturnType2 {
return average
}
I've used typealias to create two example types. Could a type be overloaded somehow to handle both situations? To me, this makes sense in that if the same function is being overloaded to handle different inputs, why not the type? Perhaps this is a naive perspective or perhaps there is a way to express what I'm thinking in Swift?

How to reference the function? Just specify the type!
func doMath(mathOption: String) -> (Int...) -> Double {
return { (values: Int...) -> Double in
return Double(values.reduce(0, +)) / Double(values.count)
}
}
func doMath(mathOption: String) -> ([Int]) -> Double {
return { (values: [Int]) -> Double in
return Double(values.reduce(0, +)) / Double(values.count)
}
}
let average1 = doMath(mathOption: "x") as (Int...) -> Double
print(average1(1, 2, 3))
or
let average1: (Int...) -> Double = doMath(mathOption: "x")
print(average1(1, 2, 3))
I would also advise to to name that type using a typealias.
Your second question - you cannot name parameters in function types.

You can pass the function you want done to doMath as a parameter. And use generics so you have some extensibility.
func doMath<T>(using op: (T) -> Double, with value: T) -> Double {
return op(value)
}
doMath(using: sumAll, with: [1,2,3])
// returns 6
Edit: It's having trouble with the variadic parameter.
Another edit: Found a workaround.
func doMath<T>(using op: ([T]) -> Double, with value: T...) -> Double {
return op(value)
}
func doMath<T>(using op: (T) -> Double, with value: T) -> Double {
return op(value)
}
doMath(using: sumAll, with: 1,2,3,4,5) //15
doMath(using: sumAll, with: [1,2,3,4,5]) // 15
Also, here's a prettier way to write that reduce:
Double(nums.reduce(0, +))

Related

How do I write the not/negate higher order function in swift?

I am a Javascripter and I love using the not/negate function:
function not (predicateFunc) {
return function () {
return !predicateFunc.apply(this, arguments);
};
}
I am trying to do the same thing with swift:
func not <A> (_ f: #escaping (_ A: Any) -> Bool) -> (A) -> Bool {
return { a in !f(a) }
}
But I am getting errors like
generic parameter 'T' could not be inferred
and
Cannot convert value of type '(_) -> Bool' to expected argument type '(Any) -> Bool'
The outcome I am looking for is when I have a function like this:
func isEmpty<T: Collection>(collection: T) -> Bool {
return collection.count == 0
}
I can just create a notEmpty function like this:
let notEmpty = not(isEmpty)
And then use it like
notEmpty([3,4,5]) // true
What am I doing wrong?
Using Any is a code smell. You can just extend Collection directly:
extension Collection {
var notEmpty: Bool {
return !isEmpty
}
}
[1, 3, 5].notEmpty // true
Your functional definition of not can work like this:
func not <A> (_ f: #escaping (_ a: A) -> Bool) -> (A) -> Bool {
return { a in !f(a) }
}
But to call it you would need something like this:
let arrayNotEmpty = not { (array: [Int]) in array.isEmpty }
arrayNotEmpty([1, 3, 5]) // true
You have two errors:
You're using A as both the type parameter and as the argument name.
You're using Any as the argument type instead of using the type parameter (A) as the argument type.
Try this:
func not<A>(predicate: #escaping (A) -> Bool) -> (A) -> Bool {
return { !predicate($0) }
}
Note that in this version, I'm not using argument names for the predicate argument. You don't need an argument name in the declaration ((A) -> Bool) and I'm using the anonymous argument name ($0) in the body.
Okay, so you want to write this:
func isEmpty<T: Collection>(collection: T) -> Bool {
return collection.count == 0
}
func not<A>(_ predicate: #escaping (A) -> Bool) -> (A) -> Bool {
return { !predicate($0) }
}
let notEmpty = not(isEmpty)
And you get this error:
let notEmpty = not(isEmpty)
^ Generic parameter 'A' could not be inferred
The problem is that this code tries to create a generic closure, but Swift doesn't support generic closures.
That is to say, what would the type of nonEmpty be? It would be something like:
<A: Collection>(A) -> Bool
and Swift doesn't support that.

is there a more elegant syntax for Swift Filter with 2 parameters

Is there a more elegant way to filter with an additional parameter (or map, reduce).
When I filter with a single parameter, we get a beautiful easy to ready syntax
let numbers = Array(1...10)
func isGreaterThan5(number:Int) -> Bool {
return number > 5
}
numbers.filter(isGreaterThan5)
However, if I need to pass an additional parameter to my function it turns out ugly
func isGreaterThanX(number:Int,x:Int) -> Bool {
return number > x
}
numbers.filter { (number) -> Bool in
isGreaterThanX(number: number, x: 8)
}
I would like to use something like
numbers.filter(isGreaterThanX(number: $0, x: 3))
but this gives a compile error annonymous closure argument not contained in a closure
You could change your function to return a closure which serves
as predicate for the filter method:
func isGreaterThan(_ lowerBound: Int) -> (Int) -> Bool {
return { $0 > lowerBound }
}
let filtered = numbers.filter(isGreaterThan(5))
isGreaterThan is a function taking an Int argument and returning
a closure of type (Int) -> Bool. The returned closure "captures"
the value of the given lower bound.
If you make the function generic then it can be used with
other comparable types as well:
func isGreaterThan<T: Comparable>(_ lowerBound: T) -> (T) -> Bool {
return { $0 > lowerBound }
}
print(["D", "C", "B", "A"].filter(isGreaterThan("B")))
In this particular case however, a literal closure is also easy to read:
let filtered = numbers.filter( { $0 > 5 })
And just for the sake of completeness: Using the fact that
Instance Methods are Curried Functions in Swift, this would work as well:
extension Comparable {
func greaterThanFilter(value: Self) -> Bool {
return value > self
}
}
let filtered = numbers.filter(5.greaterThanFilter)
but the "reversed logic" might be confusing.
Remark: In earlier Swift versions you could use a curried function
syntax:
func isGreaterThan(lowerBound: Int)(value: Int) -> Bool {
return value > lowerBound
}
but this feature has been removed in Swift 3.

How to check/predicate function type in Swift?

For example:
func f(x: Int) -> Int {
return x
}
func h(f: #escaping (Int) -> Any) {
if (f is (Int) -> Int) {
print(f(1))
} else {
print("invalid")
}
}
h(f: f)
I expect it to print out 1 but it actually prints out invalid.
There's a workaround using generics:
func intF(x: Int) -> Int {
return x
}
func stringF(x: Int) -> String {
return "\(x)"
}
func h<T>(f: (Int) -> T) {
if (T.self == Int.self) {
print(f(1))
} else {
print("invalid")
}
}
h(f: intF) // prints: 1
h(f: stringF) // prints: invalid
Using Any is almost always a sign of code smell, you should try to rely as much as possible of the type safety that Swift provides. You can achieve this by making h generic, thus verifiable at compile time.
// the overload that does the actual stuff
func h(f: #escaping (Int) -> Int) {
print(f(1))
}
// this maps to all other types
func h<T>(f: #escaping (Int) -> T) {
print("invalid")
}
h { _ in return "15" } // Invalid
h { 2 * $0 } // 2
Heck, you could even give up the generic overload, thus you'll have for free compile checks instead of runtime failures (much, much reliable and predictive)
You can rewrite h into a generic function:
func h<T>(f: #escaping (Int) -> T) {
if T.self == Int.self {
print(f(1))
} else {
print("invalid")
}
}
But the better way to to write type-specific overloads for h and a generic catch-all for the rest (if you need it at all).

Curried closures in Swift3?

Why
let arr = [1,2,3,4,5]
let filtered = arr.filter { $0 < 3 }
and why not?
let filtered = arr.filter(<3)
if I can use operator function:
[1,2,3].sorted(by: >)
The signatures of Sequence:s filter(...) and sorted(...) are as follows
func filter(_ isIncluded: (Self.Iterator.Element) throws -> Bool) rethrows -> [Self.Iterator.Element]
func sorted(by areInIncreasingOrder: (Self.Iterator.Element, Self.Iterator.Element) -> Bool) -> [Self.Iterator.Element]
Both methods expect a closure as their argument; the former one of type (Self.Iterator.Element) -> Bool, and the latter a one of type (Self.Iterator.Element, Self.Iterator.Element) -> Bool). < is function fulfilling the latter for Comparable types (specifically (Int, Int) -> Bool in your example), whereas <3 isn't a closure at all.
You could define your own function specifically for this purpose (thanks #vacawama)
func lessThan(_ value: Int) -> ((Int) -> Bool) {
return { $0 < value }
}
let arr = [1,2,3,4,5]
let filtered = arr.filter(lessThan(3))
print(filtered) // [1, 2]
But generally it might be more simple to just supply a closure on the fly to higher order functions such as filter and sorted.

Contextual type inference for Type in Swift 2.2+

I want to use first order citizen Type in Swift to decide which function to call.
func sf(v: [Float]) -> Float{
}
func df(v: [Double]) -> Double {
}
func f<T: RealType>(seq ls: [T]) -> T {
if T.self == Float.self {
return sf(ls) // 1. where sf: Float -> Void
} else if T.self == Double.self {
return df(ls) // 2. where df : Double -> Void
}
}
The type inference system couldn't notice that under one branch T == Float and Double in the other ?
Is here a missing feature, complex feature or bug ?
Edit:
typealias RealType = protocol<FloatingPointType, Hashable, Equatable, Comparable, FloatLiteralConvertible, SignedNumberType>
for my prototype but will become a protocol
You are trying to combine static resolution given by generic with runtime decisions, and this is not possible.
You can simply overload f for both Float and Double to obtain what you need:
func f(seq ls: [Float]) -> Float {
return sf(ls) // 1. where sf: Float -> Void
}
func f(seq ls: [Double]) -> Double {
return df(ls) // 2. where df : Double -> Void
}
However, if you want RealType to be a generic placeholder that you can use over other types than Float or, Double, then you can do something like this:
protocol RealType {
static func processArray(v: [Self]) -> Self
}
extension Float: RealType {
static func processArray(v: [Float]) -> Float {
return sf(v)
}
}
extension Double: RealType {
static func processArray(v: [Double]) -> Double {
return df(v)
}
}
func sf(v: [Float]) -> Float{
return 0
}
func df(v: [Double]) -> Double {
return 0
}
func f<T: RealType>(seq ls: [T]) -> T {
return T.processArray(ls)
}
This will give you both type safety, which is one of Swift's main advantages, and scalability as whenever you need to add support for f over another type, you need to only declare that type as conforming to RealType, and implement the processArray method.