From e7a525910fecc81fcc309c7e5fe23063aecc4d65 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Thu, 22 Oct 2015 12:49:41 -0400 Subject: [PATCH 1/8] Implement anamorphism over Free. --- prototype/Doubt/Free.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/prototype/Doubt/Free.swift b/prototype/Doubt/Free.swift index ab3b90e10..2cd8adbdd 100644 --- a/prototype/Doubt/Free.swift +++ b/prototype/Doubt/Free.swift @@ -115,6 +115,15 @@ public enum Free: CustomDebugStringConvertible, SyntaxConvertible { } +// MARK: - Anamorphism + +extension Free { + public func ana(f: Seed -> Syntax)(_ seed: Seed) -> Free { + return (Roll <<< { $0.map(self.ana(f)) } <<< f)(seed) + } +} + + extension Free where B: PatchType, B.Element == Cofree { public typealias Term = B.Element From d4744200aec280bf181c9c9b967a1c2062ce6fe1 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Thu, 22 Oct 2015 12:51:26 -0400 Subject: [PATCH 2/8] Rename the parameter to `transform`. --- prototype/Doubt/Free.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prototype/Doubt/Free.swift b/prototype/Doubt/Free.swift index 2cd8adbdd..dc959930e 100644 --- a/prototype/Doubt/Free.swift +++ b/prototype/Doubt/Free.swift @@ -118,8 +118,8 @@ public enum Free: CustomDebugStringConvertible, SyntaxConvertible { // MARK: - Anamorphism extension Free { - public func ana(f: Seed -> Syntax)(_ seed: Seed) -> Free { - return (Roll <<< { $0.map(self.ana(f)) } <<< f)(seed) + public func ana(transform: Seed -> Syntax)(_ seed: Seed) -> Free { + return (Roll <<< { $0.map(self.ana(transform)) } <<< transform)(seed) } } From f145bb4293ab243547b610cd89dfbea20aaca350 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Thu, 22 Oct 2015 12:53:18 -0400 Subject: [PATCH 3/8] Document `ana`. --- prototype/Doubt/Free.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prototype/Doubt/Free.swift b/prototype/Doubt/Free.swift index dc959930e..0f50aafda 100644 --- a/prototype/Doubt/Free.swift +++ b/prototype/Doubt/Free.swift @@ -118,6 +118,9 @@ public enum Free: CustomDebugStringConvertible, SyntaxConvertible { // MARK: - Anamorphism extension Free { + /// Anamorphism over `Free`. + /// + /// Unfolds a tree bottom-up by recursively applying `transform` to a series of values starting with `seed`. Since `Syntax.Leaf` does not recur, this will halt when it has produced leaves for every branch. public func ana(transform: Seed -> Syntax)(_ seed: Seed) -> Free { return (Roll <<< { $0.map(self.ana(transform)) } <<< transform)(seed) } From 6bcbccfe086f091858be18259a130adee86064e3 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Thu, 22 Oct 2015 12:55:06 -0400 Subject: [PATCH 4/8] Document `coiterate` as an anamorphism. --- prototype/Doubt/Cofree.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/Doubt/Cofree.swift b/prototype/Doubt/Cofree.swift index 6078fc1c2..db4398805 100644 --- a/prototype/Doubt/Cofree.swift +++ b/prototype/Doubt/Cofree.swift @@ -21,7 +21,7 @@ public enum Cofree { /// Constructs a cofree by coiteration. /// - /// The initial seed is used as the annotation of the returned value. The continuation of the structure is unpacked by applying `annotate` to the seed and mapping the resulting syntax’s values recursively. In this manner, the structure is unfolded bottom-up, starting with `seed` and ending at the leaves. + /// This is an _anamorphism_ (from the Greek “ana,” “upwards”; compare “anabolism”), a generalization of unfolds over regular trees (and datatypes isomorphic to them). The initial seed is used as the annotation of the returned value. The continuation of the structure is unpacked by applying `annotate` to the seed and mapping the resulting syntax’s values recursively. In this manner, the structure is unfolded bottom-up, starting with `seed` and ending at the leaves. /// /// As this is the dual of `Free.iterate`, it’s unsurprising that we have a similar guarantee: coiteration is linear in the size of the constructed tree. public static func coiterate(annotate: B -> Syntax)(_ seed: B) -> Cofree { From 89adf2cc867fa1f3e491b7e29fbfd479d708a974 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Thu, 22 Oct 2015 13:05:08 -0400 Subject: [PATCH 5/8] Add a static `unwrap` function over `TermType`. --- prototype/Doubt/TermType.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prototype/Doubt/TermType.swift b/prototype/Doubt/TermType.swift index 06408d430..45f951371 100644 --- a/prototype/Doubt/TermType.swift +++ b/prototype/Doubt/TermType.swift @@ -7,6 +7,10 @@ public protocol TermType { extension TermType { + public static func unwrap(term: Self) -> Syntax { + return term.unwrap + } + /// Catamorphism over `TermType`s. /// /// Folds the tree encoded by the receiver into a single value by recurring top-down through the tree, applying `transform` to leaves, then to branches, and so forth. From 4cc87bc0cf051ee628167ef2d2a9caead6ce9d5a Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Thu, 22 Oct 2015 13:05:16 -0400 Subject: [PATCH 6/8] Tacit unwrapping. --- prototype/Doubt/TermType.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prototype/Doubt/TermType.swift b/prototype/Doubt/TermType.swift index 45f951371..85febe2ce 100644 --- a/prototype/Doubt/TermType.swift +++ b/prototype/Doubt/TermType.swift @@ -15,14 +15,14 @@ extension TermType { /// /// Folds the tree encoded by the receiver into a single value by recurring top-down through the tree, applying `transform` to leaves, then to branches, and so forth. public func cata(transform: Syntax -> Result) -> Result { - return self |> ({ $0.unwrap } >>> { $0.map { $0.cata(transform) } } >>> transform) + return self |> (Self.unwrap >>> { $0.map { $0.cata(transform) } } >>> transform) } /// Paramorphism over `TermType`s. /// /// Folds the tree encoded by the receiver into a single value by recurring top-down through the tree, applying `transform` to leaves, then to branches, and so forth. Each recursive instance is made available in the `Syntax` alongside the result value at that node. public func para(transform: Syntax<(Self, Result), LeafType> -> Result) -> Result { - return self |> ({ $0.unwrap } >>> { $0.map { ($0, $0.para(transform)) } } >>> transform) + return self |> (Self.unwrap >>> { $0.map { ($0, $0.para(transform)) } } >>> transform) } From f36651660b280c36b04db81a2e0f52fe8029fc69 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Thu, 22 Oct 2015 13:11:00 -0400 Subject: [PATCH 7/8] `ana` is `static`. --- prototype/Doubt/Free.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prototype/Doubt/Free.swift b/prototype/Doubt/Free.swift index 0f50aafda..6c8443a9e 100644 --- a/prototype/Doubt/Free.swift +++ b/prototype/Doubt/Free.swift @@ -121,8 +121,8 @@ extension Free { /// Anamorphism over `Free`. /// /// Unfolds a tree bottom-up by recursively applying `transform` to a series of values starting with `seed`. Since `Syntax.Leaf` does not recur, this will halt when it has produced leaves for every branch. - public func ana(transform: Seed -> Syntax)(_ seed: Seed) -> Free { - return (Roll <<< { $0.map(self.ana(transform)) } <<< transform)(seed) + public static func ana(transform: Seed -> Syntax)(_ seed: Seed) -> Free { + return (Roll <<< { $0.map(ana(transform)) } <<< transform)(seed) } } From f00f3f485f23bf25f8262e1310f91ee114655df5 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Thu, 22 Oct 2015 13:15:13 -0400 Subject: [PATCH 8/8] Copy diffs recursively using `ana`. --- prototype/Doubt/Interpreter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype/Doubt/Interpreter.swift b/prototype/Doubt/Interpreter.swift index 3b327c3fe..fdf14008d 100644 --- a/prototype/Doubt/Interpreter.swift +++ b/prototype/Doubt/Interpreter.swift @@ -42,7 +42,7 @@ public struct Interpreter { /// Diff `a` against `b`, if comparable. private func recur(a: Term, _ b: Term) -> Diff? { - if equal(a, b) { return Diff(b) } + if equal(a, b) { return Diff.ana(Term.unwrap)(b) } guard comparable(a, b) else { return nil } let algorithm: Algorithm