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