2015-10-14 17:19:02 +03:00
|
|
|
|
/// The cofree comonad over `Syntax`.
|
|
|
|
|
///
|
2015-10-22 17:22:21 +03:00
|
|
|
|
/// 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 `Annotation` paired with it) to satisfy the comonad laws.
|
2015-10-14 17:19:02 +03:00
|
|
|
|
///
|
2015-10-22 17:22:21 +03:00
|
|
|
|
/// This type is dual to `Free`. Where `Free` is inhabited by syntax trees where some terms are replaced with `Annotation`s, `Cofree` is inhabited by syntax trees where all terms are annotated with `Annotation`s. In Doubt, this allows us to e.g. annotate terms with source range information, categorization, etc.
|
2015-10-22 17:23:20 +03:00
|
|
|
|
public enum Cofree<Leaf, Annotation> {
|
|
|
|
|
indirect case Unroll(Annotation, Syntax<Cofree, Leaf>)
|
2015-10-14 16:59:04 +03:00
|
|
|
|
|
2015-10-22 17:23:20 +03:00
|
|
|
|
public var unwrap: Syntax<Cofree, Leaf> {
|
2015-10-14 16:59:04 +03:00
|
|
|
|
switch self {
|
|
|
|
|
case let .Unroll(_, rest):
|
2015-10-16 23:40:47 +03:00
|
|
|
|
return rest
|
2015-10-14 16:59:04 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-10-14 17:39:01 +03:00
|
|
|
|
|
|
|
|
|
|
2015-10-22 17:23:20 +03:00
|
|
|
|
public init(_ annotation: Annotation, _ syntax: Syntax<Cofree, Leaf>) {
|
2015-10-14 21:17:29 +03:00
|
|
|
|
self = .Unroll(annotation, syntax)
|
|
|
|
|
}
|
2015-10-14 16:34:18 +03:00
|
|
|
|
}
|
2015-10-14 16:43:23 +03:00
|
|
|
|
|
|
|
|
|
|
2015-10-14 23:40:13 +03:00
|
|
|
|
// MARK: - CustomDebugStringConvertible
|
|
|
|
|
|
|
|
|
|
extension Cofree: CustomDebugStringConvertible {
|
|
|
|
|
public var debugDescription: String {
|
|
|
|
|
return "(\(String(reflecting: extract)), \(String(reflecting: unwrap)))"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-10-14 17:09:38 +03:00
|
|
|
|
// MARK: - Functor
|
|
|
|
|
|
|
|
|
|
extension Cofree {
|
2015-10-22 17:23:20 +03:00
|
|
|
|
public func map<Other>(transform: Annotation -> Other) -> Cofree<Leaf, Other> {
|
2015-10-16 23:40:47 +03:00
|
|
|
|
return .Unroll(transform(extract), 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-22 17:22:21 +03:00
|
|
|
|
public var extract: Annotation {
|
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-22 17:23:20 +03:00
|
|
|
|
public func extend<Other>(transform: Cofree -> Other) -> Cofree<Leaf, Other> {
|
2015-10-16 23:40:47 +03:00
|
|
|
|
return .Unroll(transform(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-22 17:23:20 +03:00
|
|
|
|
public var duplicate: Cofree<Leaf, Cofree<Leaf, Annotation>> {
|
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 {
|
2015-10-22 17:23:20 +03:00
|
|
|
|
public static func equals(annotation annotation: (Annotation, Annotation) -> Bool, leaf: (Leaf, Leaf) -> Bool)(_ left: Cofree, _ right: Cofree) -> Bool {
|
2015-10-14 17:52:27 +03:00
|
|
|
|
return annotation(left.extract, right.extract)
|
2015-10-22 17:46:51 +03:00
|
|
|
|
&& Syntax.equals(leaf: leaf, recur: Cofree.equals(annotation: annotation, leaf: leaf))(left.unwrap, right.unwrap)
|
2015-10-14 17:52:27 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-22 17:23:20 +03:00
|
|
|
|
public func == <Leaf: Equatable, Annotation: Equatable> (left: Cofree<Leaf, Annotation>, right: Cofree<Leaf, Annotation>) -> Bool {
|
2015-10-14 17:53:29 +03:00
|
|
|
|
return Cofree.equals(annotation: ==, leaf: ==)(left, right)
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-14 17:52:27 +03:00
|
|
|
|
|
2015-10-14 17:59:41 +03:00
|
|
|
|
// MARK: - JSON
|
|
|
|
|
|
|
|
|
|
extension Cofree {
|
2015-10-22 17:23:20 +03:00
|
|
|
|
public func JSON(annotation annotation: Annotation -> Doubt.JSON, leaf: Leaf -> Doubt.JSON) -> Doubt.JSON {
|
2015-10-14 17:59:41 +03:00
|
|
|
|
return [
|
|
|
|
|
"extract": annotation(extract),
|
2015-10-22 17:47:48 +03:00
|
|
|
|
"unwrap": unwrap.JSON(leaf: leaf, recur: { $0.JSON(annotation: annotation, leaf: leaf) })
|
2015-10-14 17:59:41 +03:00
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-22 17:23:20 +03:00
|
|
|
|
extension Cofree where Leaf: CustomJSONConvertible, Annotation: CustomJSONConvertible {
|
2015-10-14 18:01:41 +03:00
|
|
|
|
public var JSON: Doubt.JSON {
|
|
|
|
|
return JSON(annotation: { $0.JSON }, leaf: { $0.JSON })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-14 17:59:41 +03:00
|
|
|
|
|
2015-10-14 22:49:23 +03:00
|
|
|
|
// MARK: - Categorizable
|
|
|
|
|
|
2015-10-22 17:22:21 +03:00
|
|
|
|
extension Cofree where Annotation: Categorizable {
|
|
|
|
|
var categories: Set<Annotation.Category> {
|
2015-10-14 22:49:23 +03:00
|
|
|
|
return extract.categories
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-10-16 16:20:47 +03:00
|
|
|
|
// MARK: - CofreeType
|
2015-10-15 22:31:51 +03:00
|
|
|
|
|
2015-10-22 17:21:04 +03:00
|
|
|
|
public protocol CofreeType: TermType {
|
2015-10-15 22:31:51 +03:00
|
|
|
|
typealias Annotation
|
|
|
|
|
|
2015-10-23 01:02:44 +03:00
|
|
|
|
init(_ annotation: Annotation, _ syntax: Syntax<Self, Leaf>)
|
2015-10-15 22:42:24 +03:00
|
|
|
|
var extract: Annotation { get }
|
2015-10-15 22:31:51 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-23 01:04:19 +03:00
|
|
|
|
extension CofreeType {
|
|
|
|
|
public static func Wrap(annotation: Annotation)(syntax: Syntax<Self, Leaf>) -> Self {
|
|
|
|
|
return Self(annotation, syntax)
|
|
|
|
|
}
|
2015-10-23 01:04:51 +03:00
|
|
|
|
|
|
|
|
|
/// Constructs a cofree by coiteration.
|
|
|
|
|
///
|
|
|
|
|
/// 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(unfold: Annotation -> Syntax<Annotation, Leaf>)(_ seed: Annotation) -> Self {
|
|
|
|
|
return (Wrap(seed) <<< { $0.map(coiterate(unfold)) } <<< unfold) <| seed
|
|
|
|
|
}
|
2015-10-23 01:04:19 +03:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-15 22:42:39 +03:00
|
|
|
|
extension Cofree: CofreeType {}
|
|
|
|
|
|
2015-10-16 18:09:20 +03:00
|
|
|
|
extension CofreeType where Self.Annotation == Range<String.Index> {
|
|
|
|
|
public func JSON(source: String) -> Doubt.JSON {
|
|
|
|
|
return unwrap.JSON(
|
2015-10-22 17:47:48 +03:00
|
|
|
|
leaf: { _ in .String(source[extract]) },
|
|
|
|
|
recur: {
|
2015-10-16 18:09:20 +03:00
|
|
|
|
[
|
|
|
|
|
"range": [
|
|
|
|
|
"offset": .Number(Double(source.startIndex.distanceTo($0.extract.startIndex))),
|
|
|
|
|
"length": .Number(Double($0.extract.count)),
|
|
|
|
|
],
|
|
|
|
|
"unwrap": $0.JSON(source)
|
|
|
|
|
]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-15 22:31:51 +03:00
|
|
|
|
|
2015-10-14 17:24:16 +03:00
|
|
|
|
import Prelude
|