#The Design and Implementation of Extensible Variants for Rust in CGP

16 messages · Page 1 of 1 (latest)

oak tapir
#

Hi everyone, I am excited to share the fourth and final part of my blog series: Programming Extensible Data Types in Rust with Context-Generic Programming.

In this post, I dive into the implementation details of the core CGP constructs that enable extensible variants. I walk through how upcasting and downcasting operations are implemented, and how the extensible visitor pattern can be constructed using monadic pipelines. If you are curious about how structs and enums are related, or how CGP performs pattern matching on generic enums in a fully type safe manner, this post is for you.

I would also love to talk to you more about CGP and extensible variants, so join the discussion on our CGP Discord server.

Discord is great for playing games and chilling with friends, or even building a worldwide community. Customize your own space to talk, play, and hang out.

rigid pulsar
#

This is one of the best writeups I have seen for practical type theory, great job

rigid pulsar
mint wigeon
rigid pulsar
#

And it solves a problem I have with supply

mint wigeon
#

I know for a tangentially related thing, that I've sometimes used a [&T; 0] arg to a function to allow both turbofished and inferred usage:

thing::<fn() -> i32>([]);
thing([&|| 42; 0]);
rigid pulsar
#

(because stupid rustfmt)

mint wigeon
#
foo(PhantomData::<Foo>);
foo(T!(Foo));
```![ferrisHmm](https://cdn.discordapp.com/emojis/857440844171575326.webp?size=128 "ferrisHmm")
#

I haven't read the articles yet, just skimmed through the intro

#

Whilst the Has/GetField pattern was known here, I am very interested in the builder which is able to detect when every required field has been provided

oak tapir
# rigid pulsar Where does the idea of passing a PhantomData come from?

Thanks! The use of PhantomData is mainly to help with type inference, when a generic type on the trait is not used in the method. Without PhantomData, you might encounter cases where Rust is not able to infer the type due to multiple candidates available for that generic parameter. So we pass PhantomData as a way to tell Rust which type we want to use for that generic parameter.

The use of PhantomData is common in generic programming that involves types that have no useful values. You can trace its usage back to Haskell, which is defined as the Proxy type.

mint wigeon
#

?eval

trait GetField<FieldName> {
    type FieldTy;
    fn get_field(&self) -> &Self::FieldTy;
}

struct Person { name: &'static str, age: u8 }

enum name {}
enum age {}

impl GetField<name> for Person {
    type FieldTy = &'static str;
    fn get_field(&self) -> &&'static str { &self.name }
}

impl GetField<age> for Person {
    type FieldTy = u8;
    fn get_field(&self) -> &u8 { &self.age }
}

let p = Person { name: "Answer", age: 42 };
// p.get_field(); // error, ambiguous `FieldName`.
dbg!(
    // ugly
    GetField::<age>::get_field(&p),
);

trait Get {
    fn get<FieldName>(&self) -> &Self::FieldTy
    where
        Self : GetField<FieldName>,
    {
        self.get_field()
    }
}
impl<T : ?Sized> Get for T {}

dbg!(
    p.get::<name>(),
    p.get::<age>(),
);
amber marshBOT
#
[src/main.rs:24:1] GetField::<age>::get_field(&p) = 42
[src/main.rs:39:1] p.get::<name>() = "Answer"
[src/main.rs:39:1] p.get::<age>() = 42

()```