Dynamic in Rust

classic Classic list List threaded Threaded
24 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Dynamic in Rust

Oren Ben-Kiki
Is it possible to implement something like Haskell's Dynamic value holder in Rust? (This would be similar to supporting C++'s dynamic_cast). Basically, something like this:

pub struct Dynamic { ... }
impl Dynamic {
    pub fn put(value: ~T) { ... }
    pub fn get() -> Option<T> { ... }
}

I guess this would require unsafe code... even so, it seems to me that Rust pointers don't carry sufficient meta-data for the above to work. A possible workaround would be something like:

pub struct Dynamic { type_name: ~str, ... }
impl Dynamic {
    pub fn put(type_name: &str, value: ~T) { Dynamic { type_name: type_name, ... } }
    pub fn get(&'a self, type_name: &str) -> Option<&'a T> { assert_eq!(type_name, self.type_name); ... } }
}

And placing the burden on the caller to always use the type name "int" when putting or getting `int` values, etc. This would still require some sort of unsafe code to cast the `~T` pointer into something and back, while ensuring that the storage for the `T` (whatever its size is) is not released until the `Dynamic` itself is.

(Why do I need such a monstrosity? Well, I need it to define a `Configuration` container, which holds key/value pairs where whoever sets a value knows its type, whoever gets the value should ask for the same type, and the configuration can hold values of "any" type - not from a predefined list of types).

Is such a thing possible, and if so, how?

Thanks,

Oren Ben-Kiki

_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Abhijeet Gaiha

You could define an enum that encapsulates all known types.

enum monster {
     Integer(int),
     Float(float),
....
}

Then use a container for this type.

On Aug 23, 2013 10:20 AM, "Oren Ben-Kiki" <[hidden email]> wrote:
Is it possible to implement something like Haskell's Dynamic value holder in Rust? (This would be similar to supporting C++'s dynamic_cast). Basically, something like this:

pub struct Dynamic { ... }
impl Dynamic {
    pub fn put(value: ~T) { ... }
    pub fn get() -> Option<T> { ... }
}

I guess this would require unsafe code... even so, it seems to me that Rust pointers don't carry sufficient meta-data for the above to work. A possible workaround would be something like:

pub struct Dynamic { type_name: ~str, ... }
impl Dynamic {
    pub fn put(type_name: &str, value: ~T) { Dynamic { type_name: type_name, ... } }
    pub fn get(&'a self, type_name: &str) -> Option<&'a T> { assert_eq!(type_name, self.type_name); ... } }
}

And placing the burden on the caller to always use the type name "int" when putting or getting `int` values, etc. This would still require some sort of unsafe code to cast the `~T` pointer into something and back, while ensuring that the storage for the `T` (whatever its size is) is not released until the `Dynamic` itself is.

(Why do I need such a monstrosity? Well, I need it to define a `Configuration` container, which holds key/value pairs where whoever sets a value knows its type, whoever gets the value should ask for the same type, and the configuration can hold values of "any" type - not from a predefined list of types).

Is such a thing possible, and if so, how?

Thanks,

Oren Ben-Kiki

_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev


_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Oren Ben-Kiki
That would require me to declare up front which types were usable and which weren't. I'm looking for a solution where I don't need to do that; that is, allow me to add new components to the system with new configuration parameter types, without having to go to a central source location and declare these types there.


On Fri, Aug 23, 2013 at 7:54 AM, Abhijeet Gaiha <[hidden email]> wrote:

You could define an enum that encapsulates all known types.

enum monster {
     Integer(int),
     Float(float),
....
}

Then use a container for this type.

On Aug 23, 2013 10:20 AM, "Oren Ben-Kiki" <[hidden email]> wrote:
Is it possible to implement something like Haskell's Dynamic value holder in Rust? (This would be similar to supporting C++'s dynamic_cast). Basically, something like this:

pub struct Dynamic { ... }
impl Dynamic {
    pub fn put(value: ~T) { ... }
    pub fn get() -> Option<T> { ... }
}

I guess this would require unsafe code... even so, it seems to me that Rust pointers don't carry sufficient meta-data for the above to work. A possible workaround would be something like:

pub struct Dynamic { type_name: ~str, ... }
impl Dynamic {
    pub fn put(type_name: &str, value: ~T) { Dynamic { type_name: type_name, ... } }
    pub fn get(&'a self, type_name: &str) -> Option<&'a T> { assert_eq!(type_name, self.type_name); ... } }
}

And placing the burden on the caller to always use the type name "int" when putting or getting `int` values, etc. This would still require some sort of unsafe code to cast the `~T` pointer into something and back, while ensuring that the storage for the `T` (whatever its size is) is not released until the `Dynamic` itself is.

(Why do I need such a monstrosity? Well, I need it to define a `Configuration` container, which holds key/value pairs where whoever sets a value knows its type, whoever gets the value should ask for the same type, and the configuration can hold values of "any" type - not from a predefined list of types).

Is such a thing possible, and if so, how?

Thanks,

Oren Ben-Kiki

_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev



_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Abhijeet Gaiha

I think your second solution is what will work. You can use unsafe code and a void pointer combined with a type descriptor string. I would probably combine the void pointer and string in a struct.

On Aug 23, 2013 10:27 AM, "Oren Ben-Kiki" <[hidden email]> wrote:
That would require me to declare up front which types were usable and which weren't. I'm looking for a solution where I don't need to do that; that is, allow me to add new components to the system with new configuration parameter types, without having to go to a central source location and declare these types there.


On Fri, Aug 23, 2013 at 7:54 AM, Abhijeet Gaiha <[hidden email]> wrote:

You could define an enum that encapsulates all known types.

enum monster {
     Integer(int),
     Float(float),
....
}

Then use a container for this type.

On Aug 23, 2013 10:20 AM, "Oren Ben-Kiki" <[hidden email]> wrote:
Is it possible to implement something like Haskell's Dynamic value holder in Rust? (This would be similar to supporting C++'s dynamic_cast). Basically, something like this:

pub struct Dynamic { ... }
impl Dynamic {
    pub fn put(value: ~T) { ... }
    pub fn get() -> Option<T> { ... }
}

I guess this would require unsafe code... even so, it seems to me that Rust pointers don't carry sufficient meta-data for the above to work. A possible workaround would be something like:

pub struct Dynamic { type_name: ~str, ... }
impl Dynamic {
    pub fn put(type_name: &str, value: ~T) { Dynamic { type_name: type_name, ... } }
    pub fn get(&'a self, type_name: &str) -> Option<&'a T> { assert_eq!(type_name, self.type_name); ... } }
}

And placing the burden on the caller to always use the type name "int" when putting or getting `int` values, etc. This would still require some sort of unsafe code to cast the `~T` pointer into something and back, while ensuring that the storage for the `T` (whatever its size is) is not released until the `Dynamic` itself is.

(Why do I need such a monstrosity? Well, I need it to define a `Configuration` container, which holds key/value pairs where whoever sets a value knows its type, whoever gets the value should ask for the same type, and the configuration can hold values of "any" type - not from a predefined list of types).

Is such a thing possible, and if so, how?

Thanks,

Oren Ben-Kiki

_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev



_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Niko Matsakis
In reply to this post by Oren Ben-Kiki
Currently, this is not directly supported, though downcasting in
general is something we have contemplated as a feature.  It might be
possible to create some kind of horrible hack based on objects. A
trait like:

    trait Dynamic { }
    impl<T> Dynamic for T { }

would allow any value to be cast to an object. The type descriptor can
then be extracted from the vtable of the object using some rather
fragile unsafe code that will doubtless break when we change the
vtable format. The real question is what you can do with the type
descriptor; they are not canonicalized, after all. Still, it's
... very close.  This is basically how dynamic downcasting would work,
in any case.


Niko

On Fri, Aug 23, 2013 at 07:49:57AM +0300, Oren Ben-Kiki wrote:

> Is it possible to implement something like Haskell's Dynamic value holder
> in Rust? (This would be similar to supporting C++'s dynamic_cast).
> Basically, something like this:
>
> pub struct Dynamic { ... }
> impl Dynamic {
>     pub fn put(value: ~T) { ... }
>     pub fn get() -> Option<T> { ... }
> }
>
> I guess this would require unsafe code... even so, it seems to me that Rust
> pointers don't carry sufficient meta-data for the above to work. A possible
> workaround would be something like:
>
> pub struct Dynamic { type_name: ~str, ... }
> impl Dynamic {
>     pub fn put(type_name: &str, value: ~T) { Dynamic { type_name:
> type_name, ... } }
>     pub fn get(&'a self, type_name: &str) -> Option<&'a T> {
> assert_eq!(type_name, self.type_name); ... } }
> }
>
> And placing the burden on the caller to always use the type name "int" when
> putting or getting `int` values, etc. This would still require some sort of
> unsafe code to cast the `~T` pointer into something and back, while
> ensuring that the storage for the `T` (whatever its size is) is not
> released until the `Dynamic` itself is.
>
> (Why do I need such a monstrosity? Well, I need it to define a
> `Configuration` container, which holds key/value pairs where whoever sets a
> value knows its type, whoever gets the value should ask for the same type,
> and the configuration can hold values of "any" type - not from a predefined
> list of types).
>
> Is such a thing possible, and if so, how?
>
> Thanks,
>
> Oren Ben-Kiki

> _______________________________________________
> Rust-dev mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/rust-dev

_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Oren Ben-Kiki
Yes, this would be similar to the `Typeable` type class in Haskell. It queries the vtable-equivalent, which contains stuff like the name of the type and allows doing `typeof(x)`, dynamic casts, etc. This is heavily magical (that is, depends on the hidden internal representation) and properly belongs in the standard platform and not in a user-level library.


On Fri, Aug 23, 2013 at 4:40 PM, Niko Matsakis <[hidden email]> wrote:
Currently, this is not directly supported, though downcasting in
general is something we have contemplated as a feature.  It might be
possible to create some kind of horrible hack based on objects. A
trait like:

    trait Dynamic { }
    impl<T> Dynamic for T { }

would allow any value to be cast to an object. The type descriptor can
then be extracted from the vtable of the object using some rather
fragile unsafe code that will doubtless break when we change the
vtable format. The real question is what you can do with the type
descriptor; they are not canonicalized, after all. Still, it's
... very close.  This is basically how dynamic downcasting would work,
in any case.


Niko

On Fri, Aug 23, 2013 at 07:49:57AM +0300, Oren Ben-Kiki wrote:
> Is it possible to implement something like Haskell's Dynamic value holder
> in Rust? (This would be similar to supporting C++'s dynamic_cast).
> Basically, something like this:
>
> pub struct Dynamic { ... }
> impl Dynamic {
>     pub fn put(value: ~T) { ... }
>     pub fn get() -> Option<T> { ... }
> }
>
> I guess this would require unsafe code... even so, it seems to me that Rust
> pointers don't carry sufficient meta-data for the above to work. A possible
> workaround would be something like:
>
> pub struct Dynamic { type_name: ~str, ... }
> impl Dynamic {
>     pub fn put(type_name: &str, value: ~T) { Dynamic { type_name:
> type_name, ... } }
>     pub fn get(&'a self, type_name: &str) -> Option<&'a T> {
> assert_eq!(type_name, self.type_name); ... } }
> }
>
> And placing the burden on the caller to always use the type name "int" when
> putting or getting `int` values, etc. This would still require some sort of
> unsafe code to cast the `~T` pointer into something and back, while
> ensuring that the storage for the `T` (whatever its size is) is not
> released until the `Dynamic` itself is.
>
> (Why do I need such a monstrosity? Well, I need it to define a
> `Configuration` container, which holds key/value pairs where whoever sets a
> value knows its type, whoever gets the value should ask for the same type,
> and the configuration can hold values of "any" type - not from a predefined
> list of types).
>
> Is such a thing possible, and if so, how?
>
> Thanks,
>
> Oren Ben-Kiki

> _______________________________________________
> Rust-dev mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/rust-dev



_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Matthieu Monrocq
One question:

Do you only want to retrieve the exact type that was passed in, or would you want to be able to extract an impl that matches the type actually contained ?

The latter is more difficult to implement (dynamic_cast goes through hoops to check those things), but it is doable if sufficient information is encoded in the v-table.


On Fri, Aug 23, 2013 at 5:04 PM, Oren Ben-Kiki <[hidden email]> wrote:
Yes, this would be similar to the `Typeable` type class in Haskell. It queries the vtable-equivalent, which contains stuff like the name of the type and allows doing `typeof(x)`, dynamic casts, etc. This is heavily magical (that is, depends on the hidden internal representation) and properly belongs in the standard platform and not in a user-level library.


On Fri, Aug 23, 2013 at 4:40 PM, Niko Matsakis <[hidden email]> wrote:
Currently, this is not directly supported, though downcasting in
general is something we have contemplated as a feature.  It might be
possible to create some kind of horrible hack based on objects. A
trait like:

    trait Dynamic { }
    impl<T> Dynamic for T { }

would allow any value to be cast to an object. The type descriptor can
then be extracted from the vtable of the object using some rather
fragile unsafe code that will doubtless break when we change the
vtable format. The real question is what you can do with the type
descriptor; they are not canonicalized, after all. Still, it's
... very close.  This is basically how dynamic downcasting would work,
in any case.


Niko

On Fri, Aug 23, 2013 at 07:49:57AM +0300, Oren Ben-Kiki wrote:
> Is it possible to implement something like Haskell's Dynamic value holder
> in Rust? (This would be similar to supporting C++'s dynamic_cast).
> Basically, something like this:
>
> pub struct Dynamic { ... }
> impl Dynamic {
>     pub fn put(value: ~T) { ... }
>     pub fn get() -> Option<T> { ... }
> }
>
> I guess this would require unsafe code... even so, it seems to me that Rust
> pointers don't carry sufficient meta-data for the above to work. A possible
> workaround would be something like:
>
> pub struct Dynamic { type_name: ~str, ... }
> impl Dynamic {
>     pub fn put(type_name: &str, value: ~T) { Dynamic { type_name:
> type_name, ... } }
>     pub fn get(&'a self, type_name: &str) -> Option<&'a T> {
> assert_eq!(type_name, self.type_name); ... } }
> }
>
> And placing the burden on the caller to always use the type name "int" when
> putting or getting `int` values, etc. This would still require some sort of
> unsafe code to cast the `~T` pointer into something and back, while
> ensuring that the storage for the `T` (whatever its size is) is not
> released until the `Dynamic` itself is.
>
> (Why do I need such a monstrosity? Well, I need it to define a
> `Configuration` container, which holds key/value pairs where whoever sets a
> value knows its type, whoever gets the value should ask for the same type,
> and the configuration can hold values of "any" type - not from a predefined
> list of types).
>
> Is such a thing possible, and if so, how?
>
> Thanks,
>
> Oren Ben-Kiki

> _______________________________________________
> Rust-dev mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/rust-dev



_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev



_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Oren Ben-Kiki
That's a really good question. In my specific case, I need the exact same type. But a "proper" implementation would presumably be able to provide "any matching type" (I believe the Haskell implementation does this).


On Fri, Aug 23, 2013 at 7:17 PM, Matthieu Monrocq <[hidden email]> wrote:
One question:

Do you only want to retrieve the exact type that was passed in, or would you want to be able to extract an impl that matches the type actually contained ?

The latter is more difficult to implement (dynamic_cast goes through hoops to check those things), but it is doable if sufficient information is encoded in the v-table.


On Fri, Aug 23, 2013 at 5:04 PM, Oren Ben-Kiki <[hidden email]> wrote:
Yes, this would be similar to the `Typeable` type class in Haskell. It queries the vtable-equivalent, which contains stuff like the name of the type and allows doing `typeof(x)`, dynamic casts, etc. This is heavily magical (that is, depends on the hidden internal representation) and properly belongs in the standard platform and not in a user-level library.


On Fri, Aug 23, 2013 at 4:40 PM, Niko Matsakis <[hidden email]> wrote:
Currently, this is not directly supported, though downcasting in
general is something we have contemplated as a feature.  It might be
possible to create some kind of horrible hack based on objects. A
trait like:

    trait Dynamic { }
    impl<T> Dynamic for T { }

would allow any value to be cast to an object. The type descriptor can
then be extracted from the vtable of the object using some rather
fragile unsafe code that will doubtless break when we change the
vtable format. The real question is what you can do with the type
descriptor; they are not canonicalized, after all. Still, it's
... very close.  This is basically how dynamic downcasting would work,
in any case.


Niko

On Fri, Aug 23, 2013 at 07:49:57AM +0300, Oren Ben-Kiki wrote:
> Is it possible to implement something like Haskell's Dynamic value holder
> in Rust? (This would be similar to supporting C++'s dynamic_cast).
> Basically, something like this:
>
> pub struct Dynamic { ... }
> impl Dynamic {
>     pub fn put(value: ~T) { ... }
>     pub fn get() -> Option<T> { ... }
> }
>
> I guess this would require unsafe code... even so, it seems to me that Rust
> pointers don't carry sufficient meta-data for the above to work. A possible
> workaround would be something like:
>
> pub struct Dynamic { type_name: ~str, ... }
> impl Dynamic {
>     pub fn put(type_name: &str, value: ~T) { Dynamic { type_name:
> type_name, ... } }
>     pub fn get(&'a self, type_name: &str) -> Option<&'a T> {
> assert_eq!(type_name, self.type_name); ... } }
> }
>
> And placing the burden on the caller to always use the type name "int" when
> putting or getting `int` values, etc. This would still require some sort of
> unsafe code to cast the `~T` pointer into something and back, while
> ensuring that the storage for the `T` (whatever its size is) is not
> released until the `Dynamic` itself is.
>
> (Why do I need such a monstrosity? Well, I need it to define a
> `Configuration` container, which holds key/value pairs where whoever sets a
> value knows its type, whoever gets the value should ask for the same type,
> and the configuration can hold values of "any" type - not from a predefined
> list of types).
>
> Is such a thing possible, and if so, how?
>
> Thanks,
>
> Oren Ben-Kiki

> _______________________________________________
> Rust-dev mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/rust-dev



_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev




_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Patrick Walton
In reply to this post by Oren Ben-Kiki
On 8/23/13 8:04 AM, Oren Ben-Kiki wrote:
> Yes, this would be similar to the `Typeable` type class in Haskell. It
> queries the vtable-equivalent, which contains stuff like the name of the
> type and allows doing `typeof(x)`, dynamic casts, etc. This is heavily
> magical (that is, depends on the hidden internal representation) and
> properly belongs in the standard platform and not in a user-level library.

I had always figured we'd copy Haskell's `Data.Typeable` solution more
or less exactly. I think we can do it in a library though, as Niko
pointed out; in general we aren't afraid of adding deep
compiler-specific magic into our libraries.

Patrick

_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Danny Gratzer
I guess it's worth pointing out GHC's -XDeriveDataTypeable language extension, this let's the compiler automatically derive the Typeable instance for user defined datatypes. If you want to copy Data.Typeable having something (a macro?) to automagically implemented Typeable goes a long way to aiding usability.

I'm not sure if an equivalent to Haskell's `derive` exists in Rust. I'm rather new here :)


Cheers,
Danny Gratzer


_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Oren Ben-Kiki
Yes, Rust has a `deriving` attribute one can attach to types (for stuff like `Eq`, `Ord`, etc.).

That said, as long as the vtable is already there, I think Rust should do what Haskell is moving to (automatically `derive Typeable`) - that is, automatically place the necessary data in the vtable.

Haskell does place a restriction on _using_ the data, though. One must explicitly request the type T has the trait `Typeable` in order to invoke functionality that uses the data. Even though "all types" implement this trait, this explicitly warns the caller that "this function may do strange things". I'm not sure what the right choice would be in Rust here.


On Fri, Aug 23, 2013 at 7:44 PM, Danny Gratzer <[hidden email]> wrote:
I guess it's worth pointing out GHC's -XDeriveDataTypeable language extension, this let's the compiler automatically derive the Typeable instance for user defined datatypes. If you want to copy Data.Typeable having something (a macro?) to automagically implemented Typeable goes a long way to aiding usability.

I'm not sure if an equivalent to Haskell's `derive` exists in Rust. I'm rather new here :)


Cheers,
Danny Gratzer


_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev



_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Patrick Walton
In reply to this post by Danny Gratzer
On 8/23/13 9:44 AM, Danny Gratzer wrote:
> I guess it's worth pointing out GHC's -XDeriveDataTypeable language
> extension, this let's the compiler automatically derive the Typeable
> instance for user defined datatypes. If you want to copy Data.Typeable
> having something (a macro?) to automagically implemented Typeable goes a
> long way to aiding usability.
>
> I'm not sure if an equivalent to Haskell's `derive` exists in Rust. I'm
> rather new here :)

Yes, it's called #[deriving] and is a syntax extension. I was assuming
that we'd implement it for `Typeable`.

Patrick

_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Graydon Hoare
In reply to this post by Oren Ben-Kiki
On 13-08-23 09:49 AM, Oren Ben-Kiki wrote:
> Yes, Rust has a `deriving` attribute one can attach to types (for stuff
> like `Eq`, `Ord`, etc.).
>
> That said, as long as the vtable is already there, I think Rust should
> do what Haskell is moving to (automatically `derive Typeable`) - that
> is, automatically place the necessary data in the vtable.

Every type descriptor has a visitor-glue method written into it, which
provides typeable-like structural reflection (std::reflect) on types,
but not (presently) efficient type-equality. We'd need to augment it
slightly to provide that, but I think it'd be possible. This code is
used in fmt! for example to support printing arbitrary values (std::repr).

-Graydon

_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Oren Ben-Kiki
Interesting - I see std::intrinsics::unstable::get_tydescr<T> -> *TyDescr...

Two questions:

- Can this be used for a quick type equality check (same pointer <=> same type)?

- How can one invoke `get_tydescr<int>()` or `get_tydescr<T>` (when `T` is a type parameter of the current function)? I get a syntax error on the `int` or `T`. 


On Fri, Aug 23, 2013 at 8:50 PM, Graydon Hoare <[hidden email]> wrote:
On 13-08-23 09:49 AM, Oren Ben-Kiki wrote:
> Yes, Rust has a `deriving` attribute one can attach to types (for stuff
> like `Eq`, `Ord`, etc.).
>
> That said, as long as the vtable is already there, I think Rust should
> do what Haskell is moving to (automatically `derive Typeable`) - that
> is, automatically place the necessary data in the vtable.

Every type descriptor has a visitor-glue method written into it, which
provides typeable-like structural reflection (std::reflect) on types,
but not (presently) efficient type-equality. We'd need to augment it
slightly to provide that, but I think it'd be possible. This code is
used in fmt! for example to support printing arbitrary values (std::repr).

-Graydon



_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Graydon Hoare
On 13-08-23 11:08 AM, Oren Ben-Kiki wrote:
> Interesting - I see std::intrinsics::unstable::get_tydescr<T> -> *TyDescr...
>
> Two questions:
>
> - Can this be used for a quick type equality check (same pointer <=>
> same type)?

No, as I said, it does not support efficient type-equality. We don't
normalize all types. You should be able to count on one-way equality at
the moment (same pointer => same type) but not vice-versa.

> - How can one invoke `get_tydescr<int>()` or `get_tydescr<T>` (when `T`
> is a type parameter of the current function)? I get a syntax error on
> the `int` or `T`.

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.

-Graydon

_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Patrick Walton
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.

Patrick

_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Oren Ben-Kiki
In reply to this post by Graydon Hoare
Got it, thanks. I guess that the TyDescr could be extended in the future to support type equality and other things. In the meanwhile, in my case I'll have the user manually annotate the Dynamic with an (interned) string containing the type name, and pray people don't mess it up.

Thanks!


On Fri, Aug 23, 2013 at 9:15 PM, Graydon Hoare <[hidden email]> wrote:
On 13-08-23 11:08 AM, Oren Ben-Kiki wrote:
> Interesting - I see std::intrinsics::unstable::get_tydescr<T> -> *TyDescr...
>
> Two questions:
>
> - Can this be used for a quick type equality check (same pointer <=>
> same type)?

No, as I said, it does not support efficient type-equality. We don't
normalize all types. You should be able to count on one-way equality at
the moment (same pointer => same type) but not vice-versa.

> - How can one invoke `get_tydescr<int>()` or `get_tydescr<T>` (when `T`
> is a type parameter of the current function)? I get a syntax error on
> the `int` or `T`.

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.

-Graydon



_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Graydon Hoare
In reply to this post by Patrick Walton
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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Michael Sullivan-2
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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev


_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Dynamic in Rust

Michael Sullivan-2
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 <[hidden email]> 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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev



_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
12