I'm new to Swift so please let me know if I've missed something painful obvious. I have a class that I want to pass by value to overload the + operator.
The code won't work if I define the left argument lhs as foo but then it is immutable, and will work if lhs is inout foo, but then I have modified lhs which I clearly do not want.
A quick breakdown of my class:
class foo<T: Numeric> {
/* Data */
/* Init Fn */
/* += definition */
static func + (lhs: foo, rhs: foo) -> foo {
do {
try lhs += rhs
return lhs
} catch {
/* Error Handling */
}
}
}
I come from a C++ background, so I am surprised that I am unable to pass the object by value if I choose. Following the question What are the basic rules and idioms for operator overloading?, in C++ this overloading method would expect the left argument to be passed by value and the right argument to be passed by const & as shown below, but here I don't seem to have that option.
class X {
/* In Swift operators are not defined internally like this */
X& operator+=(const X& rhs) {
// actual addition of rhs to *this
return *this;
}
};
inline X operator+(X lhs, const X& rhs) {
lhs += rhs;
return lhs;
}
Is there a way that I don't know about, or is overloading done differently in Swift?
Any help would be greatly appreciated.
I don't see any real problem with mutability. Note that with classes, without passing-by-value, you just cannot use one operator to define the other one.
class Foo<T: Numeric> {
var value: T
init(value: T) {
self.value = value
}
static func + (lhs: Foo, rhs: Foo) -> Foo {
return Foo(value: lhs.value + rhs.value)
}
static func += (lhs: Foo, rhs: Foo) {
lhs.value += rhs.value
}
}
let ten = Foo<Int>(value: 10)
let eighteen = ten + Foo<Int>(value: 8)
eighteen += Foo<Int>(value: 1)
The definition of the multiply symbol is this.
public func *(lhs: Int, rhs: Int) -> Int
I can use it in a function, like this.
func productUsingReduce(xs: [Int]) -> Int {
return xs.reduce(1, { x,y in x * y})
}
or simply like this.
func productUsingReduce(xs: [Int]) -> Int {
return xs.reduce(1, *)
}
If I try to define the same thing with a different name.
func yes(lhs: Int, rhs: Int) -> Int {
return lhs * rhs
}
I get a compiler error when I try to use it like this.
func productUsingReduce(xs: [Int]) -> Int {
return xs.reduce(1, { x,y in x yes y})
}
Why?
And why doesn't the following sytnax compile?
func productUsingReduce(xs: [Int]) -> Int {
return xs.reduce(1, { x, y in *(lhs: x, rhs: y) })
}
Your function func yes(.. is just a standard function.
To define an operator you have to declare the operator
infix operator yes
and the corresponding function
func yes(lhs: Int, rhs: Int) -> Int {
return lhs * rhs
}
But now comes the bad news: You cannot use alphanumeric characters as operators.
* definition is made of two parts:
The method definition (in Swift.Math.Integers, Swift.Math.Floating and so on)
public func *(lhs: Int, rhs: Int) -> Int
and its operator behavior (in Swift, you can write import Swift in Xcode and CMD+click on it to access the file)
infix operator * : MultiplicationPrecedence
The last snippet makes * an infix operator, so you can only use it in infix form.
About your yes function, you cannot make it behave as an operator. While you can define your own custom operators, they can only contain certain characters. As per the language specification:
Custom operators can begin with one of the ASCII characters /, =, -, +, !, *, %, <, >, &, |, ^, ?, or ~, or one of the Unicode characters defined in the grammar below (which include characters from the Mathematical Operators, Miscellaneous Symbols, and Dingbats Unicode blocks, among others). After the first character, combining Unicode characters are also allowed.
So you have to use it its standard function form.
But you could do
func ^^^(lhs: Int, rhs: Int) -> Int {
return lhs * rhs
}
infix operator ^^^
and that would work as expected
3 ^^^ 2 // 6
I want to define a new operator and multiply each element of the array [Int] by Int, such as [3, 2, 10] * 10.
However, because Int is neither protocol nor class (it's struct), I first defined the following:
protocol Numeric {}
extension Int: Numeric {}
extension Double: Numeric {}
extension Float: Numeric {}
And then, I tried defining the operator, like:
func *<T: Numeric> (left: [T], right: T) -> [T] {
var newArray: [T] = []
for i in left {
newArray.append(i * right)
}
return newArray
}
However, this spits out an error: Cannot convert value of type 'T' to expected argument type '[_]'.
I'm not sure what the type [_] means, which I don't expect, but I guess the problem comes from that I don't have an operator defined that takes T and T, both of which are Numeric in this case.
So I defined another operator, like:
func *<T: Numeric> (left: T, right: T) -> T {
return left * right
}
However, while this has been compiled without problems, the runtime error occurred with a lot of a lot of static * infix <A where ...> (A, A) -> A.
I'm not sure why this operator was executed so many times, but now I wonder if it is possible in the first place to define a custom * operator, although the Int does already have * operator defined.
So is it still possible to define [Int] * Int operator in Swift?
You have to require the multiplication operation in the Numeric
protocol:
protocol Numeric {
func *(lhs: Self, rhs: Self) -> Self
}
Otherwise the multiplication in newArray.append(i * right) is
not defined.
Your
func *<T: Numeric> (left: T, right: T) -> T {
return left * right
}
(which calls itself recursively, resulting in a stack overflow) is then not needed.
The implementation of your new operator itself can be simplified
(as already described in a now deleted answer) to
func *<T: Numeric> (left: [T], right: T) -> [T] {
return left.map { $0 * right }
}
I implemented a helper to have an array of unowned references:
class Unowned<T: AnyObject>
{
unowned var value : T
init (value: T) { self.value = value }
func get() -> T { return self.value }
}
Now, it is possible to do [ Unowned<Foo> ]. However, I'm not satisfied with having the additional get() method to retrieve the underlying object. So, I wanted to write a custom binary operator, e.g. --> for being able to do
for unownedFoo in ArrayOfUnownedFoos
{
var bar : Int = unownedFoo-->method()
}
My current approach is to define
infix operator --> { }
func --><T> (inout lhs: Unowned<T>, inout rhs: () -> Int) -> Int
{
}
The idea I had behind this is:
lhs is obvisouly the object I get out of the array, on which I want to perform the call on
rhs is the method I desire to call. In this case method() would not take no parameters and return an Int, and therefore
The return value is int.
However, the following problems / uncertainties arise:
Is this the correct approach?
Are my assumptions above correct?
How can I call the provided closure rhs on the instance of the extracted Unowned<T>, e.g. (pseudocode) lhs.value.rhs(). If method() was static, I could do T.method(lhs.value), but then I would have to extract the name of the method somehow to make it more generic.
Maybe, a postfix operator is rather simple.
postfix operator * {}
postfix func *<T>(v:Unowned<T>) -> T {
return v.value
}
// Usage:
for unownedFoo in ArrayOfUnownedFoos {
var bar : Int = unownedFoo*.method()
}
Use something like:
func --> <T:AnyObject, V> (lhs: Unowned<T>, rhs: (T) -> V) -> V
{
return rhs (lhs.get())
}
and then use it as:
for unownedFoo in ArrayOfUnownedFoos
{
var bar : Int = unownedFoo-->{ (val:Int) in return 2*val }
}
Specifically, you don't want to use method() as that itself is a function call - which you probably don't want unless method() is actually returning a closure.
Swift lets you create an Array extension that sums Integer's with:
extension Array {
func sum() -> Int {
return self.map { $0 as Int }.reduce(0) { $0 + $1 }
}
}
Which can now be used to sum Int[] like:
[1,2,3].sum() //6
But how can we make a generic version that supports summing other Number types like Double[] as well?
[1.1,2.1,3.1].sum() //fails
This question is NOT how to sum numbers, but how to create a generic Array Extension to do it.
Getting Closer
This is the closest I've been able to get if it helps anyone get closer to the solution:
You can create a protocol that can fulfills what we need to do, i.e:
protocol Addable {
func +(lhs: Self, rhs: Self) -> Self
init()
}
Then extend each of the types we want to support that conforms to the above protocol:
extension Int : Addable {
}
extension Double : Addable {
}
And then add an extension with that constraint:
extension Array {
func sum<T : Addable>(min:T) -> T
{
return self.map { $0 as T }.reduce(min) { $0 + $1 }
}
}
Which can now be used against numbers that we've extended to support the protocol, i.e:
[1,2,3].sum(0) //6
[1.1,2.1,3.1].sum(0.0) //6.3
Unfortunately I haven't been able to get it working without having to supply an argument, i.e:
func sum<T : Addable>(x:T...) -> T?
{
return self.map { $0 as T }.reduce(T()) { $0 + $1 }
}
The modified method still works with 1 argument:
[1,2,3].sum(0) //6
But is unable to resolve the method when calling it with no arguments, i.e:
[1,2,3].sum() //Could not find member 'sum'
Adding Integer to the method signature also doesn't help method resolution:
func sum<T where T : Integer, T: Addable>() -> T?
{
return self.map { $0 as T }.reduce(T()) { $0 + $1 }
}
But hopefully this will help others come closer to the solution.
Some Progress
From #GabrielePetronella answer, it looks like we can call the above method if we explicitly specify the type on the call-site like:
let i:Int = [1,2,3].sum()
let d:Double = [1.1,2.2,3.3].sum()
As of Swift 2 it's possible to do this using protocol extensions. (See The Swift Programming Language: Protocols for more information).
First of all, the Addable protocol:
protocol Addable: IntegerLiteralConvertible {
func + (lhs: Self, rhs: Self) -> Self
}
extension Int : Addable {}
extension Double: Addable {}
// ...
Next, extend SequenceType to add sequences of Addable elements:
extension SequenceType where Generator.Element: Addable {
var sum: Generator.Element {
return reduce(0, combine: +)
}
}
Usage:
let ints = [0, 1, 2, 3]
print(ints.sum) // Prints: "6"
let doubles = [0.0, 1.0, 2.0, 3.0]
print(doubles.sum) // Prints: "6.0"
I think I found a reasonable way of doing it, borrowing some ideas from scalaz and starting from your proposed implementation.
Basically what we want is to have typeclasses that represents monoids.
In other words, we need:
an associative function
an identity value (i.e. a zero)
Here's a proposed solution, which works around the swift type system limitations
First of all, our friendly Addable typeclass
protocol Addable {
class func add(lhs: Self, _ rhs: Self) -> Self
class func zero() -> Self
}
Now let's make Int implement it.
extension Int: Addable {
static func add(lhs: Int, _ rhs: Int) -> Int {
return lhs + rhs
}
static func zero() -> Int {
return 0
}
}
So far so good. Now we have all the pieces we need to build a generic `sum function:
extension Array {
func sum<T : Addable>() -> T {
return self.map { $0 as T }.reduce(T.zero()) { T.add($0, $1) }
}
}
Let's test it
let result: Int = [1,2,3].sum() // 6, yay!
Due to limitations of the type system, you need to explicitly cast the result type, since the compiler is not able to figure by itself that Addable resolves to Int.
So you cannot just do:
let result = [1,2,3].sum()
I think it's a bearable drawback of this approach.
Of course, this is completely generic and it can be used on any class, for any kind of monoid.
The reason why I'm not using the default + operator, but I'm instead defining an add function, is that this allows any type to implement the Addable typeclass. If you use +, then a type which has no + operator defined, then you need to implement such operator in the global scope, which I kind of dislike.
Anyway, here's how it would work if you need for instance to make both Int and String 'multipliable', given that * is defined for Int but not for `String.
protocol Multipliable {
func *(lhs: Self, rhs: Self) -> Self
class func m_zero() -> Self
}
func *(lhs: String, rhs: String) -> String {
return rhs + lhs
}
extension String: Multipliable {
static func m_zero() -> String {
return ""
}
}
extension Int: Multipliable {
static func m_zero() -> Int {
return 1
}
}
extension Array {
func mult<T: Multipliable>() -> T {
return self.map { $0 as T }.reduce(T.m_zero()) { $0 * $1 }
}
}
let y: String = ["hello", " ", "world"].mult()
Now array of String can use the method mult to perform a reverse concatenation (just a silly example), and the implementation uses the * operator, newly defined for String, whereas Int keeps using its usual * operator and we only need to define a zero for the monoid.
For code cleanness, I much prefer having the whole typeclass implementation to live in the extension scope, but I guess it's a matter of taste.
In Swift 2, you can solve it like this:
Define the monoid for addition as protocol
protocol Addable {
init()
func +(lhs: Self, rhs: Self) -> Self
static var zero: Self { get }
}
extension Addable {
static var zero: Self { return Self() }
}
In addition to other solutions, this explicitly defines the zero element using the standard initializer.
Then declare Int and Double as Addable:
extension Int: Addable {}
extension Double: Addable {}
Now you can define a sum() method for all Arrays storing Addable elements:
extension Array where Element: Addable {
func sum() -> Element {
return self.reduce(Element.zero, combine: +)
}
}
Here's a silly implementation:
extension Array {
func sum(arr:Array<Int>) -> Int {
return arr.reduce(0, {(e1:Int, e2:Int) -> Int in return e1 + e2})
}
func sum(arr:Array<Double>) -> Double {
return arr.reduce(0, {(e1:Double, e2:Double) -> Double in return e1 + e2})
}
}
It's silly because you have to say arr.sum(arr). In other words, it isn't encapsulated; it's a "free" function sum that just happens to be hiding inside Array. Thus I failed to solve the problem you're really trying to solve.
3> [1,2,3].reduce(0, +)
$R2: Int = 6
4> [1.1,2.1,3.1].reduce(0, +)
$R3: Double = 6.3000000000000007
Map, Filter, Reduce and more
From my understanding of the swift grammar, a type identifier cannot be used with generic parameters, only a generic argument. Hence, the extension declaration can only be used with a concrete type.
It's doable based on prior answers in Swift 1.x with minimal effort:
import Foundation
protocol Addable {
func +(lhs: Self, rhs: Self) -> Self
init(_: Int)
init()
}
extension Int : Addable {}
extension Int8 : Addable {}
extension Int16 : Addable {}
extension Int32 : Addable {}
extension Int64 : Addable {}
extension UInt : Addable {}
extension UInt8 : Addable {}
extension UInt16 : Addable {}
extension UInt32 : Addable {}
extension UInt64 : Addable {}
extension Double : Addable {}
extension Float : Addable {}
extension Float80 : Addable {}
// NSNumber is a messy, fat class for ObjC to box non-NSObject values
// Bit is weird
extension Array {
func sum<T : Addable>(min: T = T(0)) -> T {
return map { $0 as! T }.reduce(min) { $0 + $1 }
}
}
And here: https://gist.github.com/46c1d4d1e9425f730b08
Swift 2, as used elsewhere, plans major improvements, including exception handling, promises and better generic metaprogramming.
Help for anyone else struggling to apply the extension to all Numeric values without it looking messy:
extension Numeric where Self: Comparable {
/// Limits a numerical value.
///
/// - Parameter range: The range the value is limited to be in.
/// - Returns: The numerical value clipped to the range.
func limit(to range: ClosedRange<Self>) -> Self {
if self < range.lowerBound {
return range.lowerBound
} else if self > range.upperBound {
return range.upperBound
} else {
return self
}
}
}