|
//: Playground - noun: a place where people can play |
|
|
|
import UIKit |
|
|
|
protocol Functor { |
|
associatedtype A |
|
associatedtype B |
|
associatedtype FB |
|
|
|
func fmap(_: @escaping (A) -> B) -> FB |
|
} |
|
|
|
protocol Monad: Functor { |
|
static func unit(f: A) -> Self |
|
func bind(f : (A) -> FB) -> FB |
|
static func >>=(x: Self, f : (A) -> FB) -> FB |
|
} |
|
|
|
infix operator >>=: AdditionPrecedence //{associativity left} |
|
func >>=<M: Monad>(x: M, f: (M.A) -> M.FB) -> M.FB { |
|
return x.bind(f: f) |
|
} |
|
func bind<M: Monad>(x: M, f: (M.A) -> M.FB) -> M.FB { |
|
return x.bind(f: f) |
|
} |
|
func unit<M: Monad>(a: M.A) -> M { |
|
return M.unit(f: a) |
|
} |
|
|
|
/** |
|
Make Array a functor |
|
*/ |
|
extension Array: Functor { |
|
typealias A = Element |
|
typealias B = Any |
|
typealias FB = [B] |
|
|
|
func fmap<B>(_ f: @escaping (A) -> B) -> [B] { |
|
return self.map(f) |
|
} |
|
} |
|
|
|
/** |
|
Make Array a monad |
|
*/ |
|
extension Array: Monad { |
|
static func unit(f: Element) -> [Element] { |
|
return [f] |
|
} |
|
|
|
func bind(f: (Element) -> Array<Array.B>) -> Array<Array.B> { |
|
let addTwo: ([Array.B], [Array.B]) -> [Array.B] = { x, y in x + y } |
|
return self.map(f).reduce([], addTwo) |
|
} |
|
} |
|
|
|
/** |
|
Make optional a functor |
|
*/ |
|
extension Optional: Functor { |
|
typealias A = Wrapped |
|
typealias B = Any |
|
typealias FB = B? |
|
|
|
func fmap<B>(_ f: @escaping (A) -> B) -> B? { |
|
return self.map(f) |
|
} |
|
} |
|
|
|
/** |
|
Make optional a monad |
|
*/ |
|
extension Optional: Monad { |
|
static func unit(f x: A) -> A? { |
|
return Optional<A>.some(x) |
|
} |
|
|
|
func bind<B>(f: (A) -> B?) -> B? { |
|
return self.flatMap(f) |
|
} |
|
} |
|
|
|
extension String: Functor { |
|
typealias A = Character |
|
typealias B = Character |
|
typealias FB = String |
|
|
|
func fmap<B>(_ f: @escaping (A) -> B) -> String { |
|
return (self.characters.map { String(f($0) as! Character) }).joined() |
|
} |
|
|
|
} |
|
|
|
extension String: Monad { |
|
static func unit(f c: A) -> String { |
|
return String(c) |
|
} |
|
|
|
func bind(f: (A) -> FB) -> String { |
|
return (self.characters.map(f)).joined() |
|
} |
|
} |
|
|
|
func square(x: Double) -> Double { |
|
return x * x |
|
} |
|
|
|
func invert(x: Double) -> Double? { |
|
return fabs(x) > 0.0 ? 1.0 / x : nil |
|
} |
|
|
|
func squareRoot(x: Double) -> Double? { |
|
return x > 0.0 ? sqrt(x) : nil |
|
} |
|
|
|
func test(x: Double) -> String { |
|
return "test: \(x)" |
|
} |
|
|
|
let lettersArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz".characters.map { $0 } |
|
|
|
func rot13(input: Character) -> Character { |
|
if let i = lettersArray.index(of: input) { |
|
return lettersArray[i + 13 % Int(lettersArray.count)] |
|
} else { |
|
return input |
|
} |
|
} |
|
|
|
/** |
|
Let's take Functor and Monad out for a spin... |
|
*/ |
|
|
|
let xs = [2.0, 3.0, 5.0, 7.0, 11.0, 13.0, 17.0] |
|
xs.fmap(square) |
|
|
|
let optionalXs: [Double?] = [2.0, nil, 5.0, 7.0, 11.0, 13.0, 17.0] |
|
let squared = optionalXs.fmap { $0.fmap(square) } |
|
print (squared) |
|
|
|
let optional2: Double? = 2 |
|
optional2.fmap(test) |
|
optional2.bind(f: squareRoot) |
|
|
|
optional2 >>= { squareRoot(x: $0) } |
|
|
|
"hello world".fmap(rot13) |
|
|
|
"hello world" >>= { unit(a: rot13(input: $0)) } |