Skip to content

Instantly share code, notes, and snippets.

@CodaFi
Last active January 6, 2024 07:23
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CodaFi/bbedc25a318a1565e4c1 to your computer and use it in GitHub Desktop.
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.
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))
}
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)
}
}
}
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