Skip to content

Instantly share code, notes, and snippets.

@Sgeo
Last active November 27, 2024 15:58
Show Gist options
  • Save Sgeo/ecee21895815fb2066e3 to your computer and use it in GitHub Desktop.
Save Sgeo/ecee21895815fb2066e3 to your computer and use it in GitHub Desktop.
Anonymous sum type prototype
use std::marker::PhantomData;
pub enum Void {}
impl Void {
fn any(&self) -> ! {
match *self {}
}
}
trait UnwrapVoid<R> {
fn unwrap_void(self) -> R;
}
impl<R> UnwrapVoid<R> for Result<R, Void> {
fn unwrap_void(self) -> R {
match self {
Ok(r) => r,
Err(void) => void.any()
}
}
}
#[allow(dead_code)]
enum Here {}
#[allow(dead_code)]
struct There<T>(PhantomData<T>);
enum Cans<Head, Tail> {
Here(Head),
There(Tail)
}
trait NewCans<Item, Where> {
fn new(item: Item) -> Self;
}
impl<Item, Tail> NewCans<Item, Here> for Cans<Item, Tail> {
fn new(item: Item) -> Self {
Cans::Here(item)
}
}
impl<Item, Head, Tail, TailIndex> NewCans<Item, There<TailIndex>> for Cans<Head, Tail>
where Tail: NewCans<Item, TailIndex> {
fn new(item: Item) -> Self {
Cans::There(NewCans::new(item))
}
}
impl<Head, Tail> Cans<Head, Tail> {
pub fn new<Item, Where>(item: Item) -> Self
where Self: NewCans<Item, Where> {
NewCans::new(item)
}
}
trait Find<T, Where> {
fn find(&self) -> Option<&T>;
fn find_mut(&mut self) -> Option<&mut T>;
}
impl<T, Tail> Find<T, Here> for Cans<T, Tail> {
fn find(&self) -> Option<&T> {
match *self {
Cans::Here(ref t) => Some(t),
Cans::There(_) => None,
}
}
fn find_mut(&mut self) -> Option<&mut T> {
match *self {
Cans::Here(ref mut t) => Some(t),
Cans::There(_) => None
}
}
}
impl<Head, T, Tail, TailIndex> Find<T, There<TailIndex>> for Cans<Head, Tail>
where Tail: Find<T, TailIndex> {
fn find(&self) -> Option<&T> {
match *self {
Cans::Here(_) => None,
Cans::There(ref there) => there.find()
}
}
fn find_mut(&mut self) -> Option<&mut T> {
match *self {
Cans::Here(_) => None,
Cans::There(ref mut there) => there.find_mut()
}
}
}
trait Select<T, R, Where> {
type Remainder;
fn select<F>(self, F) -> Result<R, Self::Remainder>
where F: FnOnce(T) -> R;
}
impl<T, R, Tail> Select<T, R, Here> for Cans<T, Tail> {
type Remainder = Tail;
fn select<F>(self, f: F) -> Result<R, Self::Remainder>
where F: FnOnce(T) -> R {
match self {
Cans::Here(t) => Ok(f(t)),
Cans::There(there) => Err(there)
}
}
}
impl<T, R, Head, Tail, TailIndex> Select<T, R, There<TailIndex>> for Cans<Head, Tail>
where Tail: Select<T, R, TailIndex> {
type Remainder = Cans<Head, Tail::Remainder>;
fn select<F>(self, f: F) -> Result<R, Self::Remainder>
where F: FnOnce(T) -> R {
match self {
Cans::Here(t) => Err(Cans::Here(t)),
Cans::There(there) => there.select(f).map_err(|tail| Cans::There(tail))
}
}
}
impl<T, R, Selectable, Index> Select<T, R, Index> for Result<R, Selectable>
where Selectable: Select<T, R, Index> {
type Remainder = Selectable::Remainder;
fn select<F>(self, f: F) -> Result<R, Self::Remainder>
where F: FnOnce(T) -> R{
self.or_else(|sel| sel.select(f))
}
}
trait Reshape<Dest, Indices> {
fn reshape(self) -> Dest;
}
impl<Dest> Reshape<Dest, ()> for Void {
fn reshape(self) -> Dest {
self.any()
}
}
impl<Dest, SHead, STail, HIndex, TIndices> Reshape<Dest, (HIndex, TIndices)> for Cans<SHead, STail>
where STail: Reshape<Dest, TIndices>,
Dest: NewCans<SHead, HIndex> {
fn reshape(self) -> Dest {
match self {
Cans::Here(shead) => NewCans::new(shead),
Cans::There(stail) => stail.reshape()
}
}
}
fn main() {
let cans: Cans<i32, Cans<String, Void>> = Cans::new("Hello".to_string());
{
let s: Option<&String> = cans.find();
let t: Option<&i32> = cans.find();
println!("{:?}", s);
println!("{:?}", t);
}
let foo = cans.select(|s: String| "It's a string!").select(|i: i32| "It's an i32!").unwrap_void();
println!("{:?}", foo);
let cans2: Cans<i32, Cans<String, Void>> = Cans::new("Hello".to_string());
let cans3: Cans<String, Cans<i32, Cans<i64, Void>>> = cans2.reshape();
}
@ay0ks
Copy link

ay0ks commented Oct 28, 2024

Could you please publish this to crates.io? Or can you grant me permission to do it if you can't or don't want to do it yourself?

@Sgeo
Copy link
Author

Sgeo commented Oct 29, 2024

I can work on uploading it to crates.io, but I can't promise to pay attention to maintenance long term.

May I ask why you want this specific code? I see some similar things on crates.io already, some of which are focused on error handling, some of which aren't.

@ay0ks
Copy link

ay0ks commented Nov 15, 2024

I can work on uploading it to crates.io, but I can't promise to pay attention to maintenance long term.

May I ask why you want this specific code? I see some similar things on crates.io already, some of which are focused on error handling, some of which aren't.

Sorry for 2 weeks of silence

This is the cleanest implementation I've seen so far, and also the smallest one. It could be refactored to be a little more better named, like Polymorph or Poly instead of Cans

@ay0ks
Copy link

ay0ks commented Nov 15, 2024

#![feature(decl_macro)]
use std::marker::PhantomData;

#[derive(Clone, Debug)]
pub enum Void {}

impl Void {
  fn any(&self) -> ! {
    match *self {}
  }
}

#[allow(dead_code)]
pub trait Unwrap<R> {
  fn unwrap(self) -> R;
}

impl<R> Unwrap<R> for Result<R, Void> {
  fn unwrap(self) -> R {
    match self {
      Ok(r) => r,
      Err(void) => void.any(),
    }
  }
}

pub enum HeadMarker {}
pub struct TailMarker<T>(PhantomData<T>);

#[derive(Clone, Debug)]
pub enum Poly_<Head, Tail> {
  Head(Head),
  Tail(Tail),
}

pub trait Poly<Item, Where> {
  fn new(item: Item) -> Self;
}

impl<Item, Tail> Poly<Item, HeadMarker> for Poly_<Item, Tail> {
  fn new(item: Item) -> Self {
    Poly_::Head(item)
  }
}

impl<Item, Head, Tail, TailIndex> Poly<Item, TailMarker<TailIndex>> for Poly_<Head, Tail>
where
  Tail: Poly<Item, TailIndex>,
{
  fn new(item: Item) -> Self {
    Poly_::Tail(Poly::new(item))
  }
}

#[allow(dead_code)]
impl<A, B> Poly_<A, B> {
  pub fn new<Item, Where>(item: Item) -> Self
  where
    Self: Poly<Item, Where>,
  {
    Poly::new(item)
  }
}

pub trait Find<T, Where> {
  /// Given that `P` is isomorphic to `poly![A, B]`, one of the following method calls 
  /// will successfully return `Some(&T)`: `P.find() as A`, `P.find() as B`
  /// 
  /// Example:
  /// ```rs
  /// use poly::{poly, Poly, Find, Void};
  /// 
  /// fn test() {
  ///   let p: poly![u32, i32] = Poly::new(123u32);
  ///   let a: Option<&u32> = p.find();
  ///   if let Some(a) = a {
  ///     println!("{a}");
  ///   }
  /// }
  /// ```
  fn find(&self) -> Option<&T>;
  fn find_mut(&mut self) -> Option<&mut T>;
}

impl<T, Tail> Find<T, HeadMarker> for Poly_<T, Tail> {
  fn find(&self) -> Option<&T> {
    match *self {
      Poly_::Head(ref t) => Some(t),
      Poly_::Tail(_) => None,
    }
  }

  fn find_mut(&mut self) -> Option<&mut T> {
    match *self {
      Poly_::Head(ref mut t) => Some(t),
      Poly_::Tail(_) => None,
    }
  }
}

impl<Head, T, Tail, TailIndex> Find<T, TailMarker<TailIndex>> for Poly_<Head, Tail>
where
  Tail: Find<T, TailIndex>,
{
  fn find(&self) -> Option<&T> {
    match *self {
      Poly_::Head(_) => None,
      Poly_::Tail(ref there) => there.find(),
    }
  }

  fn find_mut(&mut self) -> Option<&mut T> {
    match *self {
      Poly_::Head(_) => None,
      Poly_::Tail(ref mut there) => there.find_mut(),
    }
  }
}

pub trait Select<T, R, Where> {
  type Remainder;

  fn select<F>(self, _: F) -> Result<R, Self::Remainder>
  where
    F: FnOnce(T) -> R;
}

impl<T, R, Tail> Select<T, R, HeadMarker> for Poly_<T, Tail> {
  type Remainder = Tail;

  fn select<F>(self, f: F) -> Result<R, Self::Remainder>
  where
    F: FnOnce(T) -> R,
  {
    match self {
      Poly_::Head(t) => Ok(f(t)),
      Poly_::Tail(there) => Err(there),
    }
  }
}

impl<T, R, Head, Tail, TailIndex> Select<T, R, TailMarker<TailIndex>> for Poly_<Head, Tail>
where
  Tail: Select<T, R, TailIndex>,
{
  type Remainder = Poly_<Head, Tail::Remainder>;

  fn select<F>(self, f: F) -> Result<R, Self::Remainder>
  where
    F: FnOnce(T) -> R,
  {
    match self {
      Poly_::Head(t) => Err(Poly_::Head(t)),
      Poly_::Tail(there) => there.select(f).map_err(|tail| Poly_::Tail(tail)),
    }
  }
}

impl<T, R, Selectable, Index> Select<T, R, Index> for Result<R, Selectable>
where
  Selectable: Select<T, R, Index>,
{
  type Remainder = Selectable::Remainder;

  fn select<F>(self, f: F) -> Result<R, Self::Remainder>
  where
    F: FnOnce(T) -> R,
  {
    self.or_else(|sel| sel.select(f))
  }
}

pub trait Mash<Dest, Indices> {
  fn mash(self) -> Dest;
}

impl<Dest> Mash<Dest, ()> for Void {
  fn mash(self) -> Dest {
    self.any()
  }
}

impl<Dest, SHead, STail, HIndex, TIndices> Mash<Dest, (HIndex, TIndices)> for Poly_<SHead, STail>
where
  STail: Mash<Dest, TIndices>,
  Dest: Poly<SHead, HIndex>,
{
  fn mash(self) -> Dest {
    match self {
      Poly_::Head(shead) => Poly::new(shead),
      Poly_::Tail(stail) => stail.mash(),
    }
  }
}

pub macro poly {
  () => {
    $crate::poly::Void
  },

  ($head:ty $(, $tail:ty)*) => {
    $crate::poly::Poly_<$head, poly!($($tail),*) >
  }
}

For example

@ay0ks
Copy link

ay0ks commented Nov 15, 2024

Reshape could be left as is instead of Mash

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment