I’m not sure I understand everything in your post, but this is how I’d write 
you first C++ example:

    struct Numbers<IV, FV> {
        priv iv: IV,    // priv so the struct can only be constructed in this 
module
        priv fv: FV,
    }

    impl<I: Int, IV: Collection<I>, F: Float, FV: Collection<F>> Numbers<IV, 
FV> {
        pub fn new(iv: IV, fv: FV) -> Numbers<IV, FV> {
            Numbers { iv: iv, fv: fv }
        }
    }

You can also write type aliases with type parameters, but I don’t think you can 
enforce trait bounds on them afaik:

   type A<T> = (T, int);


~Brendan


On 7 Dec 2013, at 5:10 pm, David Piepgrass <qwertie...@gmail.com> wrote:

> Rust newb here. I have theoretical questions.
> 
> Recently I noticed that Higher-Kinded Types (HKTs) have been mentioned on the 
> mailing list a lot, but I had no idea what a HKT was, or what it might be 
> good for. After reading about them a little, they reminded me of C++'s 
> "template template parameters". In C++ you can almost write something like 
> this:
> 
> template <template <typename> class collection>
> struct Numbers {
>    collection<int> integers;
>    collection<float> floats;
> };
> 
> So then you can write Numbers<vector> for a structure that contains vector<T> 
> collections, and Numbers<list> for a structure that contains list<T> 
> collections. EXCEPT that it doesn't actually work, because vector<T> has two 
> template parameters (the second one, the allocator, is normally left at its 
> default). Let's ignore that, though.
> 
> So that brings me to my first question: is this what "higher-kinded types" 
> means? What is the difference, if any, between HKT and C++ "template 
> templates"?
> 
> However, as a C++ developer I never actually used a "template template" 
> parameter because I didn't know they existed for a long time. So instead I 
> would have written this, which has the same end-result:
> 
> struct VectorTrait
> {
>     template<typename T>
>     struct collection { typedef vector<T> type; };
> };
> struct ListTrait
> {
>     template<typename T>
>     struct collection { typedef list<T> type; };
> };
> 
> template<typename Traits>
> struct Numbers
> {
>     Traits::collection<int>::type integers;
>     Traits::collection<float>::type floats;
> };
> // Use Numbers<VectorTrait> for vector<T>, Numbers<ListTrait> for list<T>.
> 
> This is clunkier, but it would have been a bit simpler if C++ supported 
> templatized typedefs:
> 
> struct VectorTrait
> {
>     template<typename T> typedef vector<T> collection;
> };
> struct ListTrait
> {
>     template<typename T> typedef vector<T> collection;
> };
> 
> template<typename Traits>
> struct Numbers
> {
>     Traits::collection<int> integers;
>     Traits::collection<float> floats;
> };
> // Now write Numbers<VectorTrait> instead of Numbers<vector>,
> //           Numbers<ListTrait> instead of Numbers<list>.
> 
> I have found that because of the existence of typedef, "template template" 
> parameters are never actually necessary; so far, I've never seen a situation 
> where the typedef-based solution wasn't almost as good. Also, I have found 
> that "trait" types filled with typedefs seem to be a more general thing than 
> "template template"; they allow you to do things that would be very difficult 
> or impossible without them. For example you can use typedefs-in-a-struct to 
> create circular references among types that don't "know" about each other:
> 
> // I call this a "Combo"; I don't know if the technique has a standard name
> struct MyCombo {
>     typedef ConcreteA<Traits> A;
>     typedef ConcreteB<Traits> B;
>     typedef ConcreteC<Traits> C;
> };
> template<typename Combo>
> class ConcreteA { Combo::B* b; ... };
> template<typename Combo>
> class ConcreteB { Combo::C* c; ... };
> template<typename Combo>
> class ConcreteC { Combo::A* b; ... };
> 
> Here I've created a network of types (ConcreteA<MyCombo>, ConcreteB<MyCombo>, 
> and ConcreteC<MyCombo>) that are linked together through the "Combo" type 
> MyCombo, so the types can all use each other, but none of the types refer to 
> each other directly. This design allows you to freely swap in different 
> implementations of A, B, and C; it has similar advantages to "dependency 
> injection" or "inversion of control" in languages like Java and C#, except 
> that the linkages are all defined statically at compile-time, so no dynamic 
> dispatch is required.
> 
> Without the ability to define "typedefs", this approach is not possible at 
> all if there is a cyclic relationship. Also, if the combo declares more than 
> three types, it becomes impractical to specify all those types on the classes 
> directly as type parameters.
> 
> In C# I learned that this quickly becomes a major problem if you need to 
> parameterize on more than one or two types. I tried to do "generic" math 
> (which requires at least two type parameters due to the under-designed 
> standard libraries) and I also implemented a GiST data structure (see 
> http://en.wikipedia.org/wiki/GiST), and found out that the lack of any analog 
> to C++ typedef makes both of those tasks very clumsy, while also making the 
> code hard to read, because you end up with a rats' nest of type parameters 
> (or if you omit (otherwise necessary) type parameters, you might use lots of 
> casts instead.)
> 
> So I guess that leads me to two more questions.
> 
> 2. Does Rust have a "typedef" equivalent that can be used in this way?
> 3. Does it make sense to just suggest "just use typedefs instead of 
> Higher-Kinded Types"?
> 
> _______________________________________________
> Rust-dev mailing list
> Rust-dev@mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev

_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to