Skip to content

Instantly share code, notes, and snippets.

@snoyberg
Created December 2, 2020 08:39
Show Gist options
  • Save snoyberg/ef4e5dfbf1edc1ec831dd0fb549b30a3 to your computer and use it in GitHub Desktop.
Save snoyberg/ef4e5dfbf1edc1ec831dd0fb549b30a3 to your computer and use it in GitHub Desktop.
Transformers: Rust in disguise
#![feature(generic_associated_types)]
trait Functor {
type Unwrapped;
type Wrapped<A>: Functor;
fn map<F, B>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(Self::Unwrapped) -> B;
}
impl<A> Functor for Option<A> {
type Unwrapped = A;
type Wrapped<B> = Option<B>;
fn map<F: FnOnce(A) -> B, B>(self, f: F) -> Option<B> {
match self {
Some(x) => Some(f(x)),
None => None,
}
}
}
trait Pointed: Functor {
fn wrap(a: Self::Unwrapped) -> Self;
}
impl<A> Pointed for Option<A> {
fn wrap(a: A) -> Self {
Some(a)
}
}
trait Applicative: Pointed {
fn apply<A, B>(self, a: Self::Wrapped<A>) -> Self::Wrapped<B>
where
Self::Unwrapped: FnOnce(A) -> B;
}
impl<F> Applicative for Option<F> {
fn apply<A, B>(self, a: Self::Wrapped<A>) -> Self::Wrapped<B>
where
Self::Unwrapped: FnOnce(A) -> B
{
let f = self?;
let a = a?;
Some(f(a))
}
}
trait Monad : Applicative {
fn bind<B, F>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(Self::Unwrapped) -> Self::Wrapped<B>;
}
impl<A> Monad for Option<A> {
fn bind<B, F>(self, f: F) -> Option<B>
where
F: FnOnce(A) -> Option<B>,
{
self.and_then(f)
}
}
struct Identity<T>(T);
impl<T> Functor for Identity<T> {
type Unwrapped = T;
type Wrapped<A> = Identity<A>;
fn map<F, B>(self, f: F) -> Identity<B>
where
F: FnOnce(T) -> B
{
Identity(f(self.0))
}
}
impl<T> Pointed for Identity<T> {
fn wrap(val: T) -> Self{
Identity(val)
}
}
impl<T> Applicative for Identity<T> {
fn apply<A, B>(self, a: Identity<A>) -> Identity<B>
where
Self::Unwrapped: FnOnce(A) -> B
{
Identity((self.0)(a.0))
}
}
impl<T> Monad for Identity<T> {
fn bind<B, F>(self, f: F) -> Identity<B>
where
F: FnOnce(T) -> Identity<B>
{
f(self.0)
}
}
struct IdentityT<M>(M);
impl<M: Functor> Functor for IdentityT<M> {
type Unwrapped = M::Unwrapped;
type Wrapped<A> = IdentityT<M::Wrapped<A>>;
fn map<F, B>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(M::Unwrapped) -> B
{
IdentityT(self.0.map(f))
}
}
impl<M: Pointed> Pointed for IdentityT<M> {
fn wrap(val: M::Unwrapped) -> Self{
IdentityT(M::wrap(val))
}
}
impl<M: Applicative> Applicative for IdentityT<M> {
fn apply<A, B>(self, a: IdentityT<M::Wrapped<A>>) -> IdentityT<M::Wrapped<B>>
where
M::Unwrapped: FnOnce(A) -> B
{
IdentityT(self.0.apply(a.0))
}
}
impl<M: Monad> Monad for IdentityT<M> {
fn bind<B, F>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(Self::Unwrapped) -> Self::Wrapped<B>
{
IdentityT(self.0.bind(|x| f(x).0))
}
}
trait MonadTrans {
type Base: Monad;
fn lift(base: Self::Base) -> Self;
}
impl<M: Monad> MonadTrans for IdentityT<M> {
type Base = M;
fn lift(base: M) -> Self {
IdentityT(base)
}
}
fn main() {
let x = Some(1).bind(|x| Some(x * 2));
println!("{:?}", x);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment