amao
4/26/2015 - 7:03 PM

1-Functor-and-Monad.md

Copy and paste the swift code below into a playground to experiment.

This is a very close emulation of Functor and Monad typeclasses in swift. As of Swift 1.2 and Xcode 6.3, this is no longer very fragile.

Unfortunately, the compiler cannot verify the types when passing a function to (>>=). We have to wrap the function in a closure and call it with an explicit argument to compile.

optionalDoubles >>= squareRoot // doesn't compile
optionalDoubles >>= { squareRoot($0) } // compiles
import Foundation

protocol Functor {
    typealias A
    typealias B
    typealias FB
    
    func fmap(A -> B) -> FB
}

protocol Monad: Functor {
    static func unit(f: A) -> Self
    func bind(f : A -> FB) -> FB
    func >>=(x: Self, f : A -> FB) -> FB
}

infix operator >>= { associativity left }
func >>=<M: Monad>(x: M, f: M.A -> M.FB) -> M.FB {
    return x.bind(f)
}
func bind<M: Monad>(x: M, f: M.A -> M.FB) -> M.FB {
    return x.bind(f)
}
func unit<M: Monad>(a: M.A) -> M {
    return M.unit(a)
}

/**
Make Array a functor
*/
extension Array: Functor {
    typealias A = T
    typealias B = Any
    typealias FB = [B]
    
    func fmap<B>(f: A -> B) -> [B] {
        return self.map(f)
    }
}

/**
Make Array a monad
*/
extension Array: Monad {
    static func unit(x: A) -> [A] {
        return [x]
    }
    
    func bind<B>(f: A -> [B]) -> [B] {
        return self.map(f).reduce([], combine: +)
    }
}

/**
Make optional a functor
*/
extension Optional: Functor {
    typealias A = T
    typealias B = Any
    typealias FB = B?
    
    func fmap<B>(f: A -> B) -> B? {
        return self.map(f)
    }
}

/**
Make optional a monad
*/
extension Optional: Monad {
    static func unit(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: A -> B) -> String {
        return "".join(self.characters.map { String(f($0) as! Character) })
    }
    
}

extension String: Monad {
    static func unit(c: A) -> String {
        return String(c)
    }
    
    func bind(f: A -> FB) -> String {
        return "".join(self.characters.map(f))
    }
}

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.indexOf(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]
optionalXs.fmap { $0.fmap(square) }

let optional2: Double? = 2
optional2.fmap(test)
optional2.bind(squareRoot)

optional2 >>= { squareRoot($0) }

"hello world".fmap(rot13)

"hello world" >>= { unit(rot13($0)) }