Last active
December 6, 2019 00:27
-
-
Save bkase/86b40d80f3e44594917fd39b8fe1fe02 to your computer and use it in GitHub Desktop.
Co.swift seems to work. Read NaturalSubclass.swift for what doesn't work, Natural.swift for what seems to work, and Co_old.swift for where it breaks down
This file contains hidden or 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
import Bow | |
// This version of Co seems to work even thoug it's a bit gross to deal with Any | |
// Think of this as an example of how to deal with rank2 type polymorphism -- you only | |
// deal with Any in the constructor, even `runCo` has a generic interface. | |
// newtype Co w a = Co (forall r. w (a -> r) -> r) | |
public final class ForCo {} | |
public final class CoPartial<W: Comonad>: Kind<ForCo, W> {} | |
public typealias CoOf<W: Comonad, A> = Kind<CoPartial<W>, A> | |
/// (Co w) gives you "the best" pairing monad for any comonad w | |
/// In other words, an explorer for the state space given by w | |
public class Co<W: Comonad, A> : CoOf<W, A> { | |
internal let cow : (Kind<W, (A) -> Any>) -> Any | |
public static func fix(_ value : CoOf<W, A>) -> Co<W, A> { | |
value as! Co<W, A> | |
} | |
public static func pair() -> Pairing<W, CoPartial<W>> { | |
Pairing{ wab in | |
{ cowa in | |
Co<W, /*A*/Any>.fix(cowa).runCo(wab) | |
} | |
} | |
} | |
public init(_ cow: @escaping /*forall R.*/(Kind<W, (A) -> /*R*/Any>) -> /*R*/Any) { | |
self.cow = cow | |
} | |
public func runCo<R>(_ w: Kind<W, (A) -> R>) -> R { | |
unsafeBitCast(self.cow, to:((Kind<W, (A) -> R>) -> R).self) (w) | |
} | |
} | |
extension CoPartial: Functor { | |
public static func map<A, B>(_ fa: CoOf<W, A>, _ f: @escaping (A) -> B) -> CoOf<W, B> { | |
Co<W, B> { b in | |
Co<W, A>.fix(fa).runCo(b.map({$0 <<< f})) | |
} | |
} | |
} | |
extension CoPartial: Applicative { | |
public static func ap<A, B>(_ ff: CoOf<W, (A) -> B>, _ fa: CoOf<W, A>) -> CoOf<W, B> { | |
let f = Co<W, (A) -> B>.fix(ff).cow | |
let a = Co<W, A>.fix(fa).cow | |
return Co<W, B> { w in | |
f( | |
w.coflatMap{ wf in | |
{ g in | |
a(wf.map{$0 <<< g}) | |
} | |
} | |
) | |
} | |
} | |
public static func pure<A>(_ a: A) -> CoOf<W, A> { | |
return Co<W, A> { w in | |
w.extract()(a) | |
} | |
} | |
} | |
extension CoPartial: Monad { | |
public static func flatMap<A, B>(_ fa: CoOf<W, A>, _ f: @escaping (A) -> CoOf<W, B>) -> CoOf<W, B> { | |
let k: (Kind<W, (A) -> Any>) -> Any = Co<W, A>.fix(fa).cow | |
return Co<W, B> { w in | |
k ( | |
w.coflatMap{ wa in | |
{ a in | |
Co<W, B>.fix(f(a)).runCo(wa) | |
} | |
} | |
) | |
} | |
} | |
public static func tailRecM<A, B>(_ a: A, _ f: @escaping (A) -> Kind<CoPartial<W>, Either<A, B>>) -> Kind<CoPartial<W>, B> { | |
fatalError("TODO") | |
} | |
} |
This file contains hidden or 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
import Bow | |
/// `Co w` gives you the monad that pairs with the comonad `w`, think of it as the monad used to explore the state space induced by `w` | |
/// `newtype Co w a = Co (forall r. w (a -> r) -> r)` | |
// get the existential in there once the type constructor is fully applied | |
public final class WithWitness<X, E>{} | |
public final class ForCo {} | |
public final class CoPartial<W, E: CoExistentialWitness>: Kind<ForCo, WithWitness<W, E>> where E.W == W {} | |
public typealias CoOf<W, A, E: CoExistentialWitness> = Kind<CoPartial<W, E>, A> where E.W == W | |
public protocol CoExistentialWitness { | |
associatedtype W: Comonad | |
associatedtype A | |
func apply<R>(_ a : Kind<W, (A) -> R>) -> R | |
} | |
// in order to map, ap, bind, etc. we also have to change our witness | |
public struct MapCoExistentialWitness<E: CoExistentialWitness, B>: CoExistentialWitness { | |
public typealias W = E.W | |
public typealias A = B | |
public let witness: E | |
public let f: (E.A) -> B | |
public func apply<R>(_ b : Kind<W, (B) -> R>) -> R { | |
self.witness.apply(b.map({$0 <<< self.f})) | |
} | |
} | |
public struct ApCoExistentialWitness<E: CoExistentialWitness, B>: CoExistentialWitness { | |
public typealias W = E.W | |
public typealias A = B | |
public let witness: E | |
public let ff: CoOf<W, (E.A) -> B, MapCoExistentialWitness<E, (E.A) -> B>> | |
public func apply<R>(_ b : Kind<W, (B) -> R>) -> R { | |
let f = Co<W, (E.A) -> B, MapCoExistentialWitness<E, (E.A) -> B>>.fix(ff) | |
return f.witness.apply( | |
b.coflatMap{ wf in | |
{ g in | |
self.witness.apply(wf.map{$0 <<< g}) | |
} | |
} | |
) | |
} | |
} | |
public class Co<W, A, E: CoExistentialWitness> : CoOf<W, A, E> where E.W == W, E.A == A { | |
public let witness: E | |
public static func fix(_ value : CoOf<W, A, E>) -> Co<W, A, E> { | |
value as! Co<W, A, E> | |
} | |
public init(witness: E) { | |
self.witness = witness | |
} | |
// this is the function that we can't implement with the subclass approach as seen in Day.swift | |
// but here it works! | |
public func runCo<R>(_ w: Kind<W, (A) -> R>) -> R { | |
self.witness.apply(w) | |
} | |
} | |
// Unfortunately, we get a bit stuck here | |
extension CoPartial: Functor { | |
// this is the map we want to implement | |
// Note: `CoOf<W, A, E>` as defined above doesn't work here in place of `Kind<CoPartial<W, E>, A>` due to the where clause in the typealias (I think) | |
public static func map<A, B>(_ fa: Kind<CoPartial<W, E>, A>, _ f: @escaping (A) -> B) -> Kind<CoPartial<W, E>, B> { | |
fatalError("Can't implement the map we need") | |
} | |
// this is the one we can implement | |
// more like map on some sort of indexed functor? | |
public static func map<A, B>(_ fa: CoOf<W, A, E>, _ f: @escaping (A) -> B) -> CoOf<W, B, MapCoExistentialWitness<E, B>> where E.A == A, E.W == W { | |
Co<W, B, MapCoExistentialWitness<E, B>>(witness: MapCoExistentialWitness(witness: Co.fix(fa).witness, f: f)) | |
} | |
} | |
// it seems like Ap, Bind, etc would work if only we could deal with that annoying `E` parameter... |
This file contains hidden or 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
import Bow | |
/// Natural transformation: `type Natural f g = forall x. f x -> g x` aka `f ~> g` | |
// get the existential in there once the type constructor is fully applied | |
public final class WithWitness<X, E>{} | |
public final class ForNatural {} | |
public final class NaturalPartial<F: Functor>: Kind<ForNatural, F> {} | |
public class NaturalOf<F: Functor, G: Functor, E: NaturalExistentialWitness>: Kind<NaturalPartial<F>, WithWitness<G, E>> {} | |
public protocol NaturalExistentialWitness { | |
// the rank1 foralls go here | |
associatedtype F: Functor | |
associatedtype G: Functor | |
// the rank2 foralls go here | |
func apply<A>(_ a : Kind<F, A>) -> Kind<G, A> | |
} | |
class Natural<F, G, E: NaturalExistentialWitness>: NaturalOf<F, G, E> where E.F == F, E.G == G { | |
let witness: E | |
init(witness: E) { | |
self.witness = witness | |
} | |
public static func fix(_ value : NaturalOf<F, G, E>) -> Natural<F, G, E> { | |
value as! Natural<F, G, E> | |
} | |
func run<A>(_ a: Kind<F, A>) -> Kind<G, A> { | |
self.witness.apply(a) | |
} | |
} | |
// example | |
// existential witnesses are types | |
// witness for "Id ~> Id" | |
class NaturalWitnessIdId: NaturalExistentialWitness { | |
typealias F = ForId | |
typealias G = ForId | |
func apply<A>(_ a: IdOf<A>) -> IdOf<A> { | |
Id.fix(a) | |
} | |
} | |
let ididTransformation = Natural<ForId, ForId, NaturalWitnessIdId>(witness: NaturalWitnessIdId()) | |
// The inner forall works! | |
print(ididTransformation.run(Id<Int>(4))) | |
print(ididTransformation.run(Id<String>("hello"))) |
This file contains hidden or 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
import Bow | |
/// Natural transformation: `type Natural f g = forall x. f x -> g x` aka `f ~> g` | |
// Here we'll use the Day.swift approach, and it doesn't work | |
public final class ForNatural {} | |
public final class NaturalPartial<F: Functor>: Kind<ForNatural, F> {} | |
public typealias NaturalOf<F: Functor, G: Functor> = Kind<NaturalPartial<F>, G> | |
class Natural<F: Functor, G: Functor>: NaturalOf<F, G> { | |
// this is not really a true rank2 forall as we'll see below | |
static func from<A>(f: @escaping (Kind<F, A>) -> Kind<G, A>) -> Natural<F, G> { | |
DefaultNatural<F,G,A>(f: f) | |
} | |
// the problem is this function -- we can't just cast because `DefaultNatural` cast only succeeds if you use the same `A` that it was initialized with | |
func run<A>(a: Kind<F, A>) -> Kind<G, A> { | |
(self as! DefaultNatural<F, G, A>).f(a) | |
} | |
} | |
class DefaultNatural<F: Functor, G: Functor, A>: Natural<F, G> { | |
let f : (Kind<F, A>) -> Kind<G, A> | |
init(f: @escaping (Kind<F, A>) -> Kind<G, A>) { | |
self.f = f | |
} | |
} | |
// the compiler makes us specialize `Id` here to something (here I picked Any), | |
// but I want to say "forall a" | |
let ididTransform = Natural<ForId, ForId>.from(f: { Id<Any>.fix($0) }) | |
print(ididTransform.run(a: Id<Int>(4))) | |
// Could not cast value of type 'BowTestProject.DefaultNatural<Bow.ForId, Bow.ForId, Any>' (0x7fff98b9a518) to 'BowTestProject.DefaultNatural<Bow.ForId, Bow.ForId, Swift.Int>' (0x7fff98b9abe0). | |
print(ididTransform.run(a: Id<String>("Hello"))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment