-
-
Save eddyb/5fcbc9ee442b6ce9c63bbf1ba54e70d6 to your computer and use it in GitHub Desktop.
pub trait Object<T> {} | |
pub trait Is<Obj> where Obj: Object<Self> {} | |
impl<T, Obj: Object<T>> Is<Obj> for T {} | |
pub trait Arrow<F, In> { type Out; } | |
pub trait IsArrow<Arr, In> | |
where Arr: Arrow<Self, In, Out = Self::Out> { type Out; } | |
impl<F, Arr: Arrow<F, In>> IsArrow<Arr, In> for F { type Out = F::Out; } | |
pub trait Id<T> { | |
const id: impl IsArrow<Self, T, Out = T>; | |
} | |
pub trait Comp<Arr, R, S, T>: IsArrow<Arr, R, Out = S> { | |
fn comp(self, g: impl IsArrow<Arr, S, Out = T>) -> impl IsArrow<Arr, R, Out = T>; | |
} | |
pub trait Functor<Arr> { | |
type Obj<type>; | |
fn map<S, T>( | |
self: impl Is<Self::Obj<S>>, | |
f: impl IsArrow<Arr, S, Out = T>, | |
) -> impl Is<Self::Obj<T>>; | |
} | |
pub trait Monad<Arr>: Functor<Arr> { | |
fn pure<T>(x: T) -> impl Is<Self::Obj<T>>; | |
fn flatten(self: impl Is<Self::Obj<impl Is<Self::Obj<T>>>>) -> impl Is<Self::Obj<T>>; | |
fn flat_map<S, T>( | |
self: impl Is<Self::Obj<S>>, | |
f: impl IsArrow<Arr, S, Out: Is<Self::Obj<T>>>, | |
) -> impl Is<Self::Obj<T>> { | |
self.map(f).flatten() | |
} | |
} | |
// TODO(eddyb) have to seal a lot of these impls *somehow*. | |
pub struct TypeObj<T>; | |
impl<T> Object<T> for TypeObj<T> {} | |
pub struct FnArr; | |
impl<F: Fn(S) -> T, S, T> Arrow<F, S> for FnArr { type Out = T; } | |
impl Id<T> for FnArr { | |
const id: impl Fn(T) -> T = |x| x; | |
} | |
impl<F: Fn(R) -> S, R, S, T> Comp<FnArr, R, S, T> for F { | |
fn comp(self, g: impl Fn(S) -> T) -> impl Fn(R) -> T { | |
|x| g(self(x)) | |
} | |
} | |
pub struct FnMutArr; | |
impl<F: FnMut(S) -> T, S, T> Arrow<F, S> for FnMutArr { type Out = T; } | |
impl Id<T> for FnMutArr { | |
const id: impl FnMut(T) -> T = |x| x; | |
} | |
impl<F: FnMut(R) -> S, R, S, T> Comp<FnMutArr, R, S, T> for F { | |
fn comp(self, g: impl Fn(S) -> T) -> impl FnMut(R) -> T { | |
|x| g(self(x)) | |
} | |
} | |
pub struct FnOnceArr; | |
impl<F: FnOnce(S) -> T, S, T> Arrow<F, S> for FnOnceArr { type Out = T; } | |
impl Id<T> for FnOnceArr { | |
const id: impl FnOnce(T) -> T = |x| x; | |
} | |
impl<F: FnOnce(R) -> S, R, S, T> Comp<FnOnceArr, R, S, T> for F { | |
fn comp(self, g: impl FnOnce(S) -> T) -> impl FnOnce(R) -> T { | |
|x| g(self(x)) | |
} | |
} | |
pub struct FMOption; | |
impl Functor<FnOnceArr> for FMOption { | |
type Obj<T> = TypeObj<Option<T>>; | |
fn map<S, T>( | |
self: Option<S>, | |
f: impl FnOnce(S) -> T, | |
) -> Option<T> { | |
match self { | |
Some(x) => Some(f(x)), | |
None => None, | |
} | |
} | |
} | |
impl Monad<FnOnceArr> for FMOption { | |
fn pure<T>(x: T) -> Option<T> { Some(x) } | |
fn flatten(self: Option<Option<T>>) -> Option<T> { | |
match self { | |
Some(Some(x)) => Some(x), | |
Some(None) | None => None, | |
} | |
} | |
} | |
pub struct IterObj<T>; | |
impl<I, T> Object<I> for IterObj<T> where I: Iterator<Item = T> {} | |
pub struct FMIter; | |
impl Functor<FnMutArr> for FMIter { | |
type Obj<T> = IterObj<T>; | |
fn map<S, T, IS: Iterator<Item = S>, F: FnMut(S) -> T>( | |
self: IS, | |
f: F, | |
) -> iter::Map<IS, F> { | |
iter::Map(self, f) | |
} | |
} | |
impl Monad<FnMutArr> for FMIter { | |
fn pure<T>(x: T) -> iter::Once<T> { iter::once(x) } | |
fn flatten<N: Iterator<Item: Iterator<Item = T>>>(self: N) -> iter::Flatten<N> { | |
iter::Flatten(self) | |
} | |
} |
Tried updating this for current nightly, now that GATs are out: playground.
But it doesn't really work because Is
/IsArrow
are implied by their impl
s, but the other direction (rustc
assuming the only applicable impl
is the only one that could ever exist, and allowing assuming any unifications/bounds of the impl
).
EDIT: A bit better terminology, and removing the Foo
/IsFoo
indirection (only needed cross-crate, a bit like From
/Into
), but still totally busted for the same reason of a lack of "sealing"/"closed-world" around impl
s: playground.
EDIT2: recording a last ditch attempt of creating a more indirect "guarantor" system - that also doesn't work because there's no way to "stash a dictionary/vtable", so to speak, in Rust, at the typesystem level...
With "constraint kinds", this (maybe nicer) approach could also work: