Last active
January 6, 2024 07:23
-
-
Save CodaFi/bbedc25a318a1565e4c1 to your computer and use it in GitHub Desktop.
An encoding of recursion schemes (à "Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire" [Meijer, Fokkinga, Paterson] ~( http://www.eliza.ch/doc/meijer91functional.pdf )) in Swift.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
final class Nat<F> : FunctorOf<F> { | |
let pred : Optional<F> | |
override init() { | |
self.pred = nil | |
} | |
init(pred : F) { | |
self.pred = pred | |
} | |
func elim<B>(onZero : B, onSucc : F -> B) -> B { | |
if let p = self.pred { | |
return onSucc(p) | |
} | |
return onZero | |
} | |
override func fmap<B>(map: F -> B) -> FunctorOf<B> { | |
if let p = self.pred { | |
return Nat<B>(pred: map(p)) | |
} | |
return Nat<B>() | |
} | |
} | |
func toNat(val : UInt) -> Mu<Nat<UInt>> { | |
if val == 0 { | |
return zero() | |
} | |
return successor(toNat(val - 1)) | |
} | |
func fromNat(b : Mu<Nat<UInt>>) -> UInt { | |
func cataN<A>(phi : Nat<A> -> A) -> (Mu<Nat<A>> -> A) { | |
return { x in phi(x.unRoll.fmap(cataN(phi)) as! Nat<A>) } | |
} | |
return cataN(natPhiElim(UInt(0), { $0.successor() }))(b) | |
} | |
func zero<F>() -> Mu<Nat<F>> { | |
return Mu(Nat<Mu<Nat<F>>>()) | |
} | |
func successor<F>(n : Mu<Nat<F>>) -> Mu<Nat<F>> { | |
return Mu(Nat<Mu<Nat<F>>>(pred: n)) | |
} | |
private func natPhiElim<A>(a : A, _ f : A -> A) -> (Nat<A> -> A) { | |
return { nat in nat.elim(a, onSucc: f) } | |
} | |
func plus<F>(a : Mu<Nat<F>>, _ b : Mu<Nat<F>>) -> Mu<Nat<F>> { | |
func cataN<A>(phi : Nat<A> -> A) -> (Mu<Nat<A>> -> A) { | |
return { x in phi(x.unRoll.fmap(cataN(phi)) as! Nat<A>) } | |
} | |
return cataN(natPhiElim(a, successor))(unsafeBitCast(b, Mu<Nat<Mu<Nat<F>>>>.self)) | |
} | |
func mult<F>(a : Mu<Nat<F>>, _ b : Mu<Nat<F>>) -> Mu<Nat<F>> { | |
func cataN<A>(phi : Nat<A> -> A) -> (Mu<Nat<A>> -> A) { | |
return { x in phi(x.unRoll.fmap(cataN(phi)) as! Nat<A>) } | |
} | |
return cataN(natPhiElim(zero(), { n in plus(a, n) }))(unsafeBitCast(b, Mu<Nat<Mu<Nat<F>>>>.self)) | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
infix operator • { | |
precedence 190 | |
associativity right | |
} | |
public func • <A, B, C>(f : B -> C, g : A -> B) -> A -> C { | |
return { f(g($0)) } | |
} | |
public func id<A>(x : A) -> A { | |
return x | |
} | |
internal indirect enum Either<L, R> { | |
case Left(L) | |
case Right(R) | |
func either<C>(left : L -> C, _ right : R -> C) -> C { | |
switch self { | |
case let .Left(l): | |
return left(l) | |
case let .Right(r): | |
return right(r) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class FunctorOf<A> { | |
func fmap<B>(map : A -> B) -> FunctorOf<B> { | |
fatalError("Unimplemented ") | |
} | |
} | |
final class Mu<A> : FunctorOf<A> { | |
let unRoll : FunctorOf<Mu<A>> | |
init(_ roll : FunctorOf<Mu<A>>) { | |
self.unRoll = roll | |
} | |
func unroll() -> FunctorOf<Mu<A>> { | |
return self.unRoll | |
} | |
} | |
final class Nu<A> : FunctorOf<A> { | |
let unWrap : FunctorOf<Nu<A>> | |
init(_ wrap : FunctorOf<Nu<A>>) { | |
self.unWrap = wrap | |
} | |
func unwrap() -> FunctorOf<Nu<A>> { | |
return self.unWrap | |
} | |
} | |
func cata<A>(phi : FunctorOf<A> -> A) -> (Mu<A> -> A) { | |
return { x in phi(x.unRoll.fmap(cata(phi))) } | |
} | |
func ana<A>(phi : A -> FunctorOf<A>) -> (A -> Mu<A>) { | |
return { x in Mu(phi(x).fmap(ana(phi))) } | |
} | |
func hylo<A>(phi : FunctorOf<A> -> A, _ psi : A -> FunctorOf<A>) -> A -> A { | |
return cata(phi) • ana(psi) | |
} | |
func para<A>(phi : FunctorOf<(Mu<FunctorOf<A>>, A)> -> A, _ x : Mu<FunctorOf<A>>) -> A { | |
return phi(x.unroll().fmap(fanout(id, { para(phi, $0) }))) | |
} | |
func apo<A>(phi : A -> FunctorOf<Either<Nu<FunctorOf<A>>, A>>, _ x : A) -> Nu<FunctorOf<A>> { | |
return Nu(phi(x).fmap { e in either(id, { apo(phi, $0) }, e) }) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment