Last active
February 3, 2020 19:18
-
-
Save msullivan/6324973 to your computer and use it in GitHub Desktop.
Universal type in rust
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
// Safe implementation based on the existential power of closures. | |
mod ClosureUniversal { | |
// A value of universal type is a pair of functions. store will | |
// write the underlying value into the associated tag, while clear | |
// will erase the data in the tag to prevent space leaks. | |
pub struct Univ { | |
priv store: @fn(), | |
priv clear: @fn() | |
} | |
// A tag is a mutable option used as a scratch space to write | |
// into when inspecting a tag. | |
pub struct Tag<A> { | |
priv r: @mut Option<A> | |
} | |
pub fn new_tag<A:'static>() -> Tag<A> { | |
Tag { r: @mut None } | |
} | |
pub fn inject<A:Clone+'static>(tag: Tag<A>, x: A) -> Univ { | |
Univ { | |
store: || *tag.r = Some(x.clone()), | |
clear: || *tag.r = None | |
} | |
} | |
pub fn project<A:Clone+'static>(tag: Tag<A>, x: Univ) -> Option<A> { | |
// Cause the value to be written into its tag. If the universal | |
// value was injected with our tag, then it will be in tag.r. | |
(x.store)(); | |
// Read out the value. | |
let res = (*tag.r).clone(); | |
// Clear the value, to prevent space leaks. | |
(x.clear)(); | |
res | |
} | |
} | |
// Implementation based on explicit tags and unsafe casting. | |
mod UnsafeUniversal { | |
use std::managed; | |
use std::cast; | |
// We use pointers as our tags, since they are easy to generate | |
// uniquely and compare for equality. Uniquely generated integers | |
// might be better, but this is really simple and kind of cute. | |
type InnerTag = @(); | |
fn new_inner_tag() -> InnerTag { @() } | |
fn tag_eq(x: InnerTag, y: InnerTag) -> bool { | |
managed::ptr_eq(x, y) | |
} | |
pub struct Univ { | |
tag: InnerTag, | |
value: @() | |
} | |
pub struct Tag<A> { | |
priv inner: InnerTag | |
} | |
pub fn new_tag<A:'static>() -> Tag<A> { | |
Tag { inner: new_inner_tag() } | |
} | |
pub fn inject<A:Clone+'static>(tag: Tag<A>, x: A) -> Univ { | |
Univ { | |
tag: tag.inner, | |
value: unsafe { cast::transmute(@(x.clone())) } | |
} | |
} | |
pub fn project<A:Clone+'static>(tag: Tag<A>, x: Univ) -> Option<A> { | |
if tag_eq(tag.inner, x.tag) { | |
let ptr: @A = unsafe { cast::transmute(x.value) }; | |
Some((*ptr).clone()) | |
} else { | |
None | |
} | |
} | |
} | |
fn main() { | |
// use ClosureUniversal::*; | |
use UnsafeUniversal::*; | |
// Create some tags | |
let int_tag = new_tag::<int>(); | |
let str_tag = new_tag::<~str>(); | |
// Create some universal values with those tags | |
let u1 = inject(int_tag, 5); | |
let u2 = inject(int_tag, 6); | |
let u3 = inject(str_tag, ~"hello, world"); | |
// Try reading them | |
println(fmt!("%?", project(int_tag, u1))); // Some(5) | |
println(fmt!("%?", project(int_tag, u2))); // Some(6) | |
println(fmt!("%?", project(int_tag, u3))); // None | |
println(fmt!("%?", project(str_tag, u1))); // None | |
println(fmt!("%?", project(str_tag, u2))); // None | |
println(fmt!("%?", project(str_tag, u3))); // Some(~"hello, world") | |
// Try out a *different* int tag. | |
let int_tag2 = new_tag::<int>(); | |
// It can not be used to read things created by the other int tag | |
println(fmt!("%?", project(int_tag2, u1))); // None | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment