Skip to content

Instantly share code, notes, and snippets.

@hsavit1
Forked from mbrandonw/1-Functor-and-Monad.md
Last active August 29, 2015 14:27
Show Gist options
  • Save hsavit1/3f78bfd7065b7a8e4e14 to your computer and use it in GitHub Desktop.
Save hsavit1/3f78bfd7065b7a8e4e14 to your computer and use it in GitHub Desktop.
Swift Functor and Monad

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. However, it is very fragile (i.e. easy to crash the compiler).

For example, instance methods of fmap will run fine, but attempting to use a globally defined fmap that acts on Functor types will cause a crash. Similarly for bind. Unfortunately this means we cannot define the nice infix operator versions of these functions.

import Foundation
public struct K<A> {}
public protocol Functor {
typealias _A
typealias _B
typealias _FB = K<_B>
func fmap (_A -> _B) -> _FB
}
/**
Using this global fmap usually crashes :(
*/
public func fmap <F: Functor> (f: F._A -> F._B) -> F -> F._FB {
return { $0.fmap(f) }
}
public protocol Monad : Functor {
class func unit (f: _A) -> Self
func bind (f : _A -> _FB) -> _FB
func >>= (x: Self, f : _A -> _FB) -> _FB
}
/**
Using these global binds usually crashes :(
*/
infix operator >>= {associativity left}
public func >>= <M: Monad> (x: M, f: M._A -> M._FB) -> M._FB {
return x.bind(f)
}
public func bind <M: Monad> (x: M, f: M._A -> M._FB) -> M._FB {
return x.bind(f)
}
/**
Make Array a functor
*/
extension Array : Functor {
typealias _A = T
typealias _B = Any
typealias _FB = [_B]
public func fmap <_B> (f: _A -> _B) -> [_B] {
return self.map(f)
}
}
/**
Make Array a monad
*/
extension Array : Monad {
public static func unit (x: _A) -> [_A] {
return [x]
}
public func bind <_B> (f: _A -> [_B]) -> [_B] {
return self.map(f).reduce([], +)
}
}
/**
Make optional a functor
*/
extension Optional : Functor {
typealias _A = T
typealias _B = Any
typealias _FB = _B?
public func fmap <_B> (f: _A -> _B) -> _B? {
switch self {
case let .Some(value):
value
return f(value)
case .None:
return .None
}
}
}
/**
Make optional a monad
*/
extension Optional : Monad {
public static func unit (x: _A) -> _A? {
return Optional<_A>.Some(x)
}
public func bind <_B> (f: _A -> _B?) -> _B? {
switch self {
case let .Some(value):
return f(value)
case .None:
return .None
}
}
}
func square (x: Double) -> Double {
return x * x
}
func invert (x: Double) -> Double? {
if (x != 0.0) {
return 1.0 / x
}
return nil
}
func squareRoot (x: Double) -> Double? {
if (x < 0.0) {
return nil
}
return sqrt(x)
}
func test (x: Double) -> String {
return "test: \(x)"
}
/**
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)
// fmap(square)(xs) // crash!
let optional2: Double? = 2
optional2.fmap(test)
optional2.bind(squareRoot)
// optional2 >>= squareRoot // crash!
"done"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment