2015-10-14 16:34:18 +03:00
|
|
|
|
// Copyright © 2015 GitHub. All rights reserved.
|
|
|
|
|
|
2015-10-14 17:19:02 +03:00
|
|
|
|
/// The cofree comonad over `Syntax`.
|
|
|
|
|
///
|
|
|
|
|
/// This is “free” in the sense of “unconstrained” rather than “zero-cost”; it’s the comonad obtained by taking a functor (in this case `Syntax`) and adding the minimum necessary details (the `B` paired with it) to satisfy the comonad laws.
|
|
|
|
|
///
|
|
|
|
|
/// This type is dual to `Free`. Where `Free` is inhabited by syntax trees where some terms are replaced with `B`s, `Cofree` is inhabited by syntax trees where all terms are annotated with `B`s. In Doubt, this allows us to e.g. annotate terms with source range information, categorization, etc.
|
2015-10-14 16:43:51 +03:00
|
|
|
|
public enum Cofree<A, B> {
|
2015-10-14 17:49:05 +03:00
|
|
|
|
case Unroll(B, () -> Syntax<Cofree, A>)
|
2015-10-14 16:59:04 +03:00
|
|
|
|
|
|
|
|
|
public var unwrap: Syntax<Cofree, A> {
|
|
|
|
|
switch self {
|
|
|
|
|
case let .Unroll(_, rest):
|
2015-10-14 17:48:42 +03:00
|
|
|
|
return rest()
|
2015-10-14 16:59:04 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-10-14 17:39:01 +03:00
|
|
|
|
|
|
|
|
|
|
2015-10-14 17:39:21 +03:00
|
|
|
|
/// Recursively copies a `Fix<A>` into a `Cofree<A, B>` with a function assigning `B` for every `Fix<A>`.
|
|
|
|
|
public init(_ fix: Fix<A>, _ annotate: Fix<A> -> B) {
|
|
|
|
|
self = Cofree<A, Fix<A>>.coiterate { $0.out } (fix).map(annotate)
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-14 17:44:53 +03:00
|
|
|
|
|
|
|
|
|
/// 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.
|
|
|
|
|
///
|
|
|
|
|
/// 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.
|
2015-10-14 17:39:01 +03:00
|
|
|
|
public static func coiterate(annotate: B -> Syntax<B, A>)(_ seed: B) -> Cofree {
|
2015-10-14 17:48:42 +03:00
|
|
|
|
return .Unroll(seed, { annotate(seed).map(coiterate(annotate)) })
|
2015-10-14 17:39:01 +03:00
|
|
|
|
}
|
2015-10-14 16:34:18 +03:00
|
|
|
|
}
|
2015-10-14 16:43:23 +03:00
|
|
|
|
|
|
|
|
|
|
2015-10-14 17:09:38 +03:00
|
|
|
|
// MARK: - Functor
|
|
|
|
|
|
|
|
|
|
extension Cofree {
|
2015-10-14 17:48:42 +03:00
|
|
|
|
public func map<Other>(transform: B -> Other) -> Cofree<A, Other> {
|
|
|
|
|
return .Unroll(transform(extract), { self.unwrap.map { $0.map(transform) } })
|
2015-10-14 17:09:38 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-10-14 16:43:23 +03:00
|
|
|
|
// MARK: - Comonad
|
|
|
|
|
|
|
|
|
|
extension Cofree {
|
2015-10-14 17:19:38 +03:00
|
|
|
|
/// Returns the value annotating the syntax tree at this node.
|
2015-10-14 17:01:19 +03:00
|
|
|
|
public var extract: B {
|
2015-10-14 16:43:23 +03:00
|
|
|
|
switch self {
|
2015-10-14 16:43:51 +03:00
|
|
|
|
case let .Unroll(b, _):
|
2015-10-14 16:43:23 +03:00
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-10-14 17:00:16 +03:00
|
|
|
|
|
2015-10-14 17:22:04 +03:00
|
|
|
|
/// Returns a new `Cofree` by recursively applying `transform` to each node, producing the annotations for the copy.
|
2015-10-14 17:48:42 +03:00
|
|
|
|
public func extend<Other>(transform: Cofree -> Other) -> Cofree<A, Other> {
|
|
|
|
|
return .Unroll(transform(self), { self.unwrap.map { $0.extend(transform) } })
|
2015-10-14 17:00:16 +03:00
|
|
|
|
}
|
2015-10-14 17:03:42 +03:00
|
|
|
|
|
2015-10-14 17:23:43 +03:00
|
|
|
|
/// Returns a new `Cofree` constructed by recursively annotating each subtree with itself.
|
2015-10-14 17:03:42 +03:00
|
|
|
|
public var duplicate: Cofree<A, Cofree<A, B>> {
|
2015-10-14 17:24:16 +03:00
|
|
|
|
return extend(id)
|
2015-10-14 17:03:42 +03:00
|
|
|
|
}
|
2015-10-14 16:43:23 +03:00
|
|
|
|
}
|
2015-10-14 17:24:16 +03:00
|
|
|
|
|
|
|
|
|
|
2015-10-14 17:52:27 +03:00
|
|
|
|
// MARK: - Equality
|
|
|
|
|
|
|
|
|
|
extension Cofree {
|
|
|
|
|
public static func equals(annotation annotation: (B, B) -> Bool, leaf: (A, A) -> Bool)(_ left: Cofree, _ right: Cofree) -> Bool {
|
|
|
|
|
return annotation(left.extract, right.extract)
|
|
|
|
|
&& Syntax.equals(ifLeaf: leaf, ifRecur: Cofree.equals(annotation: annotation, leaf: leaf))(left.unwrap, right.unwrap)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-14 17:53:29 +03:00
|
|
|
|
public func == <A: Equatable, B: Equatable> (left: Cofree<A, B>, right: Cofree<A, B>) -> Bool {
|
|
|
|
|
return Cofree.equals(annotation: ==, leaf: ==)(left, right)
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-14 17:52:27 +03:00
|
|
|
|
|
2015-10-14 17:56:25 +03:00
|
|
|
|
// MARK: - Hashing
|
|
|
|
|
|
|
|
|
|
extension Cofree {
|
|
|
|
|
public func hash(annotation annotation: B -> Hash, leaf: A -> Hash) -> Hash {
|
|
|
|
|
return .Ordered([ annotation(extract), unwrap.hash(ifLeaf: leaf, ifRecur: { $0.hash(annotation: annotation, leaf: leaf) }) ])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-14 17:57:25 +03:00
|
|
|
|
extension Cofree where A: Hashable, B: Hashable {
|
|
|
|
|
public var hash: Hash {
|
|
|
|
|
return hash(annotation: Hash.init, leaf: Hash.init)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-14 17:56:25 +03:00
|
|
|
|
|
2015-10-14 17:24:16 +03:00
|
|
|
|
import Prelude
|