Skip to content

Instantly share code, notes, and snippets.

@sssbohdan
Forked from mbrandonw/1-Functor-and-Monad.md
Created October 8, 2017 19:01
Show Gist options
  • Save sssbohdan/0ab039c1795bf8078f3b3379ce151c8a to your computer and use it in GitHub Desktop.
Save sssbohdan/0ab039c1795bf8078f3b3379ce151c8a 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