Skip to content

Instantly share code, notes, and snippets.

@loganwright
Last active August 29, 2015 14:02
Show Gist options
  • Save loganwright/aa2d78a14c59c1a9fd8f to your computer and use it in GitHub Desktop.
Save loganwright/aa2d78a14c59c1a9fd8f to your computer and use it in GitHub Desktop.
// Playground - noun: a place where people can play
import Cocoa
/*
One of the new features introduced in Swift is the ability to easily add operator support to custom classes and structs. Don't worry if that doesn't make sense right away, we'll talk more about it now.
Let's start by declaring a custom struct.
*/
struct Beverage {
var liquidOunces = 0.0
var numberOfIceCubes = 0
}
/*
With this struct we are saying that we will have a beverage with some number of ounces, and some number of ice cubes. But what would happen if we wanted to mix to beverages by adding them together?
*/
let sprite = Beverage(liquidOunces: 20, numberOfIceCubes: 4);
let water = Beverage(liquidOunces: 8, numberOfIceCubes: 0);
// -- Throws error
//let spriteWaterMix = sprite + water
/*
Unfortunately, this will throw an error that says, "Could not find an overload for '+' that accepts the supplied arguments". This is because as far as Swift is concerned, it doesn't know what the plus operator should do with those arguments. That's because we haven't declared one yet.
We're going to call this an 'infix' operator because it is in between the two arguments. Let's see how this would look.
*/
@infix func + (left: Beverage, right: Beverage) -> Beverage {
let totalOunces = left.liquidOunces + right.liquidOunces
let totalIceCubes = left.numberOfIceCubes + right.numberOfIceCubes
return Beverage(liquidOunces: totalOunces, numberOfIceCubes: totalIceCubes)
}
/*
We have now declared a global function for the '+' operator that takes 'Beverage' type structs as arguments. Let's try to mix our sprite and water again.
*/
let spriteWaterMix = sprite + water
/*
'spriteWaterMix' is now a mix of the sprite and the water. Not very tasty, but it demonstrates our point.
We can use this for other operators as well. Say for example that we wanted the greater than and less than operators to work for our beverage struct.
*/
let smallGlass = Beverage(liquidOunces: 4, numberOfIceCubes: 2)
let largeGlass = Beverage(liquidOunces: 20, numberOfIceCubes: 4)
@infix func < (left: Beverage, right: Beverage) -> Bool {
return left.liquidOunces < right.liquidOunces
}
@infix func > (left: Beverage, right: Beverage) -> Bool {
return left.liquidOunces > right.liquidOunces
}
/*
As you can see, our implementation just compares liquid ounces because we don't really care about the number of ice cubes at this moment. For more complex comparisons, you can include more advanced logic. Let's see if it works!
*/
smallGlass < largeGlass
smallGlass > largeGlass
/*
We can see from our playground readout that our operators are working correctly! Before we move away from infix operators.Let's add another one to the mix that can check whether or not two beverages are equal
*/
@infix func == (left: Beverage, right: Beverage) -> Bool {
return left.liquidOunces == right.liquidOunces && left.numberOfIceCubes == right.numberOfIceCubes
}
@infix func != (left: Beverage, right: Beverage) -> Bool {
return left.liquidOunces != right.liquidOunces || left.numberOfIceCubes != right.numberOfIceCubes
}
let beverageOne = Beverage(liquidOunces: 10, numberOfIceCubes: 0)
let beverageTwo = Beverage(liquidOunces: 10, numberOfIceCubes: 0)
beverageOne == beverageTwo
beverageOne != beverageTwo
/*
We can see again from the readout that this is working correctly! You can also use infix operators for things like '*' for multiplication, '/' for division, and '%' for modulo
*/
/*
Infix operators aren't the only type available though, let's look for a moment at 'prefix' and 'postfix' operators. A common use is to allow for negative values. For example, we could do this.
*/
@prefix func - (beverage: Beverage) -> Beverage {
return Beverage(liquidOunces: -beverage.liquidOunces, numberOfIceCubes: -beverage.numberOfIceCubes);
}
/*
Obviously, it doesn't make much sense to have a negative value beverage, but it can give you an idea of how it might look.
*/
let someBeverage = Beverage(liquidOunces: 10, numberOfIceCubes: 4);
let negativeBeverage = -someBeverage
/*
We can also modify the value by declaring the operator as an assignment. A common example of this is the '++' and the '--' operator. Let's look at how that might look on our beverage struct.
*/
@prefix @assignment func -- (inout beverage: Beverage) {
beverage = Beverage(liquidOunces: beverage.liquidOunces - 1.0, numberOfIceCubes: beverage.numberOfIceCubes)
}
/*
We could use this to operate on our beverage if for example we wanted to periodically take sips from our beverage in a concise way.
*/
var soda = Beverage(liquidOunces: 20, numberOfIceCubes: 10)
--soda
soda
--soda
soda
/*
We can also use the same syntax for '++' as well. If we wanted to use the -- after the variable, we would simply declare @prefix as @postfix. Let's look at another common use of an @assignment type operator function.
*/
@assignment func += (inout left: Beverage, right: Beverage) {
left = left + right
}
/*
You can see that syntax highlighting highlights the '+' operator. This is because we already declared that operator for the 'Beverage' struct. We can now use that as needed.
*/
var orangeJuice = Beverage(liquidOunces: 4, numberOfIceCubes: 2)
var appleJuice = Beverage(liquidOunces: 8, numberOfIceCubes: 4)
/*
Now if we wanted to pour the applejuice into the orange juice, we could do this:
*/
orangeJuice += appleJuice
orangeJuice
/*
As you've probably noticed, this is an extremely powerful language feature. Although we've been using a 'struct' for this example, you can also customize operator functions for classes, and even enums! That's pretty awesome, but it doesn't even scratch the surface. We can also declare custom operators. The only limitation I've found is that we can only use these operators:
/ = - + * % < > ! & | ^ . ~
We can however combine them in any way. New operators are declared at the global lever with the prefix 'operator' and are specified to be 'infix', 'prefix', or 'postfix'. Let's look at a potential declaration.
*/
operator prefix ~~ {}
/*
That's pretty simple, but it doesn't do anything yet. Let's create an implementation for our Beverage struct. We already specified that the double minus sign operator '--' subtracts liquid ounces from our beverage, but what if we wanted something that subtracted an ice cube? Let's use the double tilda prefix
*/
@prefix func ~~ (inout beverage: Beverage) {
beverage = Beverage(liquidOunces: beverage.liquidOunces, numberOfIceCubes: beverage.numberOfIceCubes - 1)
}
var tooMuchIce = Beverage(liquidOunces: 20, numberOfIceCubes: 20)
~~tooMuchIce
tooMuchIce
~~tooMuchIce
tooMuchIce
/*
Don't worry if you thing that the 14 potential operator characters provided aren't enough. We can also use any combination of them. For example, if we wanted to remove liquid ounces AND ice, we might use this.
*/
operator prefix ~- {}
@prefix func ~- (inout beverage: Beverage) {
beverage = Beverage(liquidOunces: beverage.liquidOunces - 1.0, numberOfIceCubes: beverage.numberOfIceCubes - 1)
}
tooMuchIce
~-tooMuchIce
tooMuchIce
~-tooMuchIce
tooMuchIce
/*
Remember that nothing about our custom operators are specific to the Beverage type. Because we declared the new custom operator globally, we can impliment custom functions with that operator for any class, struct, or enum. I didn't find a limit to the number or combination of operators, so you should be able to create some pretty powerful operators within your Swift code.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment