And here is another implementation of the same interface, implemented in a
much more brute force way. It is probably more efficient that the above,
and also about 5% as elegant.
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
}
}
}
On Fri, Aug 23, 2013 at 4:54 PM, Michael Sullivan <[email protected]> wrote:
> So, as it turns out, something closely related can be implemented in
> completely safe rust.
>
> (The ideas here are based on http://mlton.org/UniversalType and the
> presentation of extensible types from my undergrad PL class. The
> implementation is based on the mlton UniversalType page, but I change the
> interface to be more like extensible types. If anybody cares, SML code for
> this is https://gist.github.com/msullivan/6324908.)
>
> We can implement a universal type that values of any (cloneable, not
> containing borrowed pointers) type can be injected to. The catch is that
> the values aren't actually keyed by the type, but by "tags" that are
> generated at runtime. This is sometimes useful (you can distinguish between
> different classes of values of the same type) and sometimes annoying (you
> have to manage the tags).
>
> The implementation here is pretty clever, but also kind of silly. It would
> probably actually be better in practice to do something unsafe internally
> with dynamically generated tag values.
>
> 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
> }
> }
>
>
> fn main() {
> use ClosureUniversal::*;
>
> // 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
> }
>
> (Code is also up at https://gist.github.com/msullivan/6324973)
>
>
> On Fri, Aug 23, 2013 at 11:22 AM, Graydon Hoare <[email protected]>wrote:
>
>> On 13-08-23 11:16 AM, Patrick Walton wrote:
>> > On 8/23/13 11:15 AM, Graydon Hoare wrote:
>> >> get_tydesc::<T>(). This is the foo<> vs foo::<> syntax papercut. Gets
>> >> everyone, sorry. It's the price of using <> for type parameters rather
>> >> than []. We need a better error message for it.
>> >
>> > Actually, [] would have the same problem, because of array indexing.
>>
>> Naturally, which is why array indexing (way back when) was foo.(index).
>> But it was a throwaway comment; this is all very much water under the
>> bridge, not in any way suggesting we revisit.
>>
>> -Graydon
>>
>> _______________________________________________
>> Rust-dev mailing list
>> [email protected]
>> https://mail.mozilla.org/listinfo/rust-dev
>>
>
>
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev