Skip to content

Instantly share code, notes, and snippets.

@DanielCardonaRojas
Last active October 30, 2017 20:50
Show Gist options
  • Save DanielCardonaRojas/22f6195cdd523436be665b286cfa7659 to your computer and use it in GitHub Desktop.
Save DanielCardonaRojas/22f6195cdd523436be665b286cfa7659 to your computer and use it in GitHub Desktop.
Monoids in Swift
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