[rust-dev] paring back the self type to be, well, just a type

Nicholas Matsakis nmatsakis at mozilla.com
Mon Apr 16 15:50:26 PDT 2012

I think that if we want to support something like the `self` type, we need to go all out and truly support higher-kinded types.  The current system is too weak to really express anything useful.  I mean, you can write useful ifaces, but you can't work with them in a generic fashion.  For example:

    iface coll<A> { fn map<B>(f: fn(A) -> B) -> self<B>; }

    fn to_int<C:coll<uint>>(c: C) -> ... {
      c.map { |i| i as int }

This cannot be compiled.  What return type should it have?  (Note that if you don't care about working with the collections generically, then you don't need to include `map` in the iface anyhow, so the self type is rather irrelevant)

Also, a word of caution: it is not at all clear that `self<T>` is the correct result type for a map operation on a collection.  For many collections, it is, but not all.  Basically it prevents collections which are specialized to types of their contents (e.g., bitsets) from implementing the collection interface.  The Scala guys took this line of reasoning to the hilt, I doubt we want to go that far (though the resulting system is very powerful).

A simpler solution is just to have a basic collection type that does not include operations to create new collections (this could be implemented by bitset), and then an extended collection type that includes mapping operations.  The latter would only be used by collections (like vector) that are fully generic with respect to the kinds of types they can store.

My current (not terribly satisfying) plan was to have `map()` return `[T]`.  Obviously not great.  Naturally, you'd end up with customized methods to different collections that returned results in a more specialized type.  It would then be impossible to work with these customized methods in a generic way which is too bad.


----- Original Message -----
From: "Marijn Haverbeke" <marijnh at gmail.com>
To: "Niko Matsakis" <niko at alum.mit.edu>
Cc: "Dave Halperin" <halperin.dr at gmail.com>, rust-dev at mozilla.org, "Niko Matsakis" <nmatsakis at mozilla.com>
Sent: Monday, April 16, 2012 3:26:03 PM
Subject: Re: [rust-dev] paring back the self type to be, well, just a type

It's not just monads that require parameterized self types. It comes
up even in something like a generic collection type, if you want to
have a map operator.

I agree that the problems you raised are real, but I think giving up
on the self type this easily would be a shame. It seems that it
wouldn't be hard to take the low-tech approach of simply spitting out
a well-defined error when one of the situations you describe occurs.
Self types are used only as a kind of templates that are filled in
when an impl implements an iface. They don't leak into the type system
in general, as far as I can see (granted, I can't see all that far,
for I am not a type theoretician).


On Fri, Apr 13, 2012 at 12:52 AM, Niko Matsakis <niko at alum.mit.edu> wrote:
> On 4/12/12 2:08 PM, Dave Halperin wrote:
>> I'm not advocating either way on this in terms of the complexity tradeoff
>> for adding a kind system, just pointing out that it's what you'd need to
>> make the current system work and it's not completely crazy to want that
>> flexibility out of the type system.
> Yes, this was one use case that the self type was intended to model (though
> not the one that I think will come up most often).  I don't think this is
> crazy by any means, but right now our type system has no notion of
> (Haskell-style) kinds and it'd be a big change to add them.
> It's possible that the self type should be yanked altogether, but it's come
> in handy for me many times, but always in its simpler incarnation of "the
> type of the receiver" rather than "the type function defined by the current
> iface".
> In any case, I spent some time trying to adapt the iface system---in any
> form!---to writing generic monadic code and I decided it's just not a very
> good fit.  There are two major hurdles.
> First, we have no good way to define a "return" function (though perhaps we
> could add static or class methods to ifaces).
> Second, and this is more major, we define monadic implementations for some
> concrete type and we should be doing it for all types.  In principle,
> something like
>    impl<A> of monad<A> for option<A> { ... }
> seems like it does what we want, but it also permits things like:
>    impl<A> of monad<uint> for option<A> { ... }
> for which there is no clear mapping from which to derive the self type.  I
> think to do this correctly, we'd rather write something like:
>    impl of monad for option { <A> ... }
> which would also fit with the "kind" notion of Haskell: here the type being
> implemented for has kind * => * and so does monad.
> Of course, we would also need to be able to write things like:
>    fn mapM<A,B,M:monad>(
>        a: [A],
>        b: fn(A) -> M<B>) -> M<[B]> { ... }
> Here the parameter M is bound to some (* => *) type constructor for which
> monad is defined.
> I am not opposed to adding such capabilities at some point.  But they don't
> feel like burning issues to me right *now*.
> Niko
> _______________________________________________
> Rust-dev mailing list
> Rust-dev at mozilla.org
> https://mail.mozilla.org/listinfo/rust-dev

More information about the Rust-dev mailing list