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

Reply via email to