Last active
October 30, 2017 20:50
-
-
Save DanielCardonaRojas/22f6195cdd523436be665b286cfa7659 to your computer and use it in GitHub Desktop.
Monoids in Swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protocol Semigroup { | |
/* A semigroup is a set and a binary operator defined for elements within that | |
set, to yeild a another element of the same type. | |
*/ | |
static func combine (_ lhs: Self, _ rhs: Self) -> Self | |
} | |
extension Semigroup { | |
// Extending a protocol enables to get free functions based on the implementation of the protocol | |
func mappend(_ with: Self) -> Self { | |
return Self.combine(self, with) | |
} | |
} | |
protocol Monoid : Semigroup { // Protocols can inherit from one another. | |
static func unit() -> Self | |
} | |
extension Monoid { | |
//Implementations for higher ordered types (kind > 1) | |
static func mconcat (_ items:[Self]) -> Self { | |
let mzero = Self.unit() | |
let result = items.reduce(mzero) { (accum, m) -> Self in | |
m.mappend(accum) | |
} | |
return result | |
} | |
//Maybe is is only a Monoid when its type parameter is also a Monoid | |
static func maybeConcat<T:Monoid>(_ m1: Maybe<T>, _ m2:Maybe<T>) -> Maybe<T> { | |
switch (m1, m2) { | |
case (Maybe.Just(let v), Maybe.Just(let v2)): | |
return Maybe.Just(v.mappend(v2)) | |
case (Maybe.Nothing, Maybe.Just(let v)): | |
return Maybe.Just(v) | |
default: | |
return m1 | |
} | |
return Maybe.Nothing | |
} | |
} | |
// Implementations | |
extension Array:Monoid { | |
static func unit() -> [Element] { | |
return [] | |
} | |
static func combine(_ lhs: Array, _ rhs: Array) -> Array{ | |
return lhs + rhs | |
} | |
} | |
extension Int: Monoid { | |
static func unit() -> Int { return 0 } | |
static func combine(_ lhs: Int,_ rhs:Int) -> Int { | |
return lhs + rhs | |
} | |
} | |
extension String : Monoid { | |
static func unit() -> String {return ""} | |
static func combine(_ lhs: String, _ rhs:String) -> String{ | |
return lhs + rhs // As you can see swift already has overloaded the + operator | |
} | |
} | |
/* | |
There can be more than one implementation of a protocol for single type. | |
Int can be a monoid under multiplication ( *, 1) and sumatiion (+, 0) | |
so the solution is to wrap them to make different types. | |
*/ | |
enum ProdNum<T : Numeric> : Monoid { //Enums can conform to protocols | |
case Prod(T) | |
static func unit () -> ProdNum { | |
return .Prod(1) | |
} | |
static func combine(_ lhs: ProdNum, _ rhs:ProdNum) -> ProdNum{ | |
switch (lhs, rhs) { | |
case (.Prod(let v), .Prod(let v2)): | |
return .Prod(v * v2) | |
} | |
} | |
} | |
enum SumNum<T : Numeric> : Monoid { //Enums can conform to protocols | |
case Sum(T) | |
static func unit () -> SumNum { | |
return .Sum(0) | |
} | |
static func combine(_ lhs: SumNum, _ rhs:SumNum) -> SumNum{ | |
switch (lhs, rhs) { | |
case (.Sum(let v), .Sum(let v2)): | |
return .Sum(v + v2) | |
} | |
} | |
} | |
enum Maybe<T> { | |
case Nothing, Just(T) | |
} | |
// ================== EXAMPLES ========================= // | |
let sumInts : ([SumNum<Int>]) -> SumNum<Int> = {list in SumNum.mconcat(list)} | |
let listOfSumNums = [1,2,3,4].map{x in SumNum.Sum(x)} | |
let listOfSumNums2 = [1.1, 2.9, 3.7, 4.8, 0].map{x in SumNum.Sum(x)} | |
SumNum.mconcat(listOfSumNums2) | |
sumInts(listOfSumNums) | |
var intList:[Int] = [1,2,3,4,5] | |
intList.mappend([9,11]) | |
Int.mconcat(intList) | |
var three:Int = 3 | |
three.mappend(4) | |
[4].mappend([6,7]) | |
Int.combine(3,5) | |
"Hello ".mappend("Daniel") | |
/* | |
Unfortunatly Swift can't handle extensions for more complex types like ((Any) -> Any) :( | |
extension (Any) -> Any : Monoid { | |
func unit() -> (Any -> Any) { | |
return {x in x} // The identity function | |
} | |
func combine(with: (Any -> Any)) { | |
return { x in with(self(x) } // Function composition | |
} | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment