Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upFinalize defaulted type parameters #213
Conversation
nikomatsakis
added some commits
Aug 26, 2014
lilyball
reviewed
Aug 26, 2014
| } | ||
|
|
||
| Using this definition, a call like `range(0, 10)` is perfectly legal. | ||
| If it turns out that the type argument is not other constraint, `uint` |
This comment has been minimized.
This comment has been minimized.
alexcrichton
assigned
nikomatsakis
Sep 4, 2014
alexcrichton
force-pushed the
rust-lang:master
branch
from
6357402
to
e0acdf4
Sep 11, 2014
This comment has been minimized.
This comment has been minimized.
|
My only concern here is about whether this feature carries its weight. Having defaults drive inference is a nice touch! But overall, it's a not insignificant amount of complexity, likely to be in conflict (or at the very least tension) with bigger and more important type system features in the future, and the benefit is not that large. The goal of backwards compatibly extending types can be accomplished with just modules and typedefs, if I'm not missing something. As merely a strawman example: Before:
After:
Existing code should keep working. Code which wants to specify a different hasher has to be modified either way, and in the course of that modification, the only additional change that has to be made would be to import The conflict with HKT we've discussed before, but there was never any satisfactory resolution proposed that I can remember. To be able to implement most of the useful HKT traits for a generic collection type, the type parameter representing the contained type needs to be the last one. For example, one would write:
If we were to add HKT later on, and then discover that we can implement (As a side note, for forwards compatibility with HKT, we should also change the order of the type parameters of |
This comment has been minimized.
This comment has been minimized.
|
@glaebhoerl We can implement HKT without relying on currying, there is no need to give anyone the wrong impression about this. Defaulted type params/VG and HKT are orthogonal (and I would like to see Haskell handling |
This comment has been minimized.
This comment has been minimized.
|
On Sat, Sep 13, 2014 at 03:54:22PM -0700, Gábor Lehel wrote:
I don't see a conflict with HKT, though clearly there is a conflict |
This comment has been minimized.
This comment has been minimized.
|
On Sat, Sep 13, 2014 at 03:54:22PM -0700, Gábor Lehel wrote:
Sorry, I didn't address this point specifically. First, let me point |
aturon
force-pushed the
rust-lang:master
branch
from
4c0bebf
to
b1d1bfd
Sep 16, 2014
nikomatsakis
referenced this pull request
Sep 19, 2014
Closed
revise fn sig and ty representation so that bot is not a type. #14973
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
Just discovered this RFC from a hint on IRC. I have an API with a function like this: fn execute<T: FromSomething>(&self) -> Result<FromSomething, Error> {
FromSomething::value_from_something(...)
}The problem comes up in cases where let _: () = execute(...).unwrap()Would it be feasible to default |
This comment has been minimized.
This comment has been minimized.
|
Actually i presume I could just define the function like this to achieve what I want: fn execute<T: FromSomething=()>(&self) -> Result<FromSomething, Error> {
FromSomething::value_from_something(...)
} |
This comment has been minimized.
This comment has been minimized.
|
On Sat, Sep 27, 2014 at 06:34:24AM -0700, Armin Ronacher wrote:
Yes, this. Though I think the result type would be |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis Sorry for the late response.
The direct conflict is indeed only syntactical as far as I can tell. As I've written before I wouldn't mind requiring an explicit syntax such as (As an aside, I don't really have any conception of what is "Rust-y" in this space, other than whatever works the best. We're not constrained the way we sometimes are at the value level.)
Newtype wrappers would of course work, but then we'd be paying back the convenience we got from default type arguments, most likely with interest. The problem with type lambdas is that they mess up type inference: see this comment by rwbarton. They also figure prominently in Edward Kmett's criticism of Scala.1 In short, they don't play well with other features and do not appear to have much of a countervailing benefit. I would rather have fewer features which work well together, than more features which are always stepping on each others' toes and negating each others' benefits. In particular, if we are to have HKTs, we should have HKTs with useful inference which makes them a practical tool that's pleasant to use, rather than HKTs as window dressing. (And even if we were to decide that we want type lambdas, that's a big decision which should be made on its own merits; getting backed into it by default type arguments would be the tail wagging the elephant.) (@eddyb: Sorry. I don't like being in the position of pushing back on someone else's pet feature, I've been on the other end and know that it sucks. But that's life. People have opinions, and they're often different ones.) 1 In case anyone's not familiar with Edward Kmett: Among many other things, he's the mastermind behind Haskell's lens library as well one of the principal contributors to scalaz, co-wrote a state-of-the-art compiler for an advanced Haskell-like language first in Scala and then in Haskell, and is quite possibly the most brilliant and prolific Haskeller on the planet. (Arguably for either taken alone; without a doubt for the combination.) I am inclined to take his opinions very seriously. |
This comment has been minimized.
This comment has been minimized.
|
@glaebhoerl I have no opinion on type lambdas as a Rust feature, but those arguments leave me unconvinced. Rust bans overlapping impls of a trait in the first place, so the same problem doesn't come up. The inability to define overlapping impls in Rust might make type lambdas less useful; I'm not sure. And even with overlapping impls, there is a good reply from winterkoininkje that went unanswered. |
This comment has been minimized.
This comment has been minimized.
|
Haskell doesn't allow overlap either, without some GHC extensions which are highly discouraged. I don't believe rwbarton was assuming them in his comment. As far as the basic issue is concerned, type classes don't even enter into it. You have a type (going with more rustic notation) And in any case... (from winterkoninkje's comment)
"Not known to be impossible" is not exactly a vote of confidence as far as adding things to Rust goes. |
This comment has been minimized.
This comment has been minimized.
|
@glaebhoerl The concerns raised by rwbarton are specifically framed in the context of choosing between multiple type class instances for a single type, which requires OverlappingInstances to be an issue in the first place. The related issue for Rust would be that impls are only allowed in modules that define either the type or the trait, so as it stands you would only be able to make impls for type lambdas in modules that define the trait, rather than those that define the type lambda. As for the more basic issue, what would be lost by just never inferring a (type-)polymorphic type? Rust already doesn't infer polymorphism when it could, e.g. this program fails to type check: fn main() {
let f = |a| a;
let _x = f(0u);
let _y = f(0i);
}Why would we expect polymorphism to be introduced at the type level when we don't introduce it at the value level? Languages that use Hindley-Milner inference and perform polymorphic generalization have this problem, but Rust doesn't. |
This comment has been minimized.
This comment has been minimized.
No, I don't believe that to be the case. The issue is confused by the fact that the prospect of type class instances for type lambdas was the suggestion being responded to, and so that's what the response also concerned itself with. But the two are logically separate. (Type inference in Haskell also precedes instance resolution.) Let's consider another example:
What's
I don't see why it makes any sense to tangle these things together. You can have a language with higher-kinded type variables but no first-class polymorphism, and you could also have the reverse. First-class polymorphism is difficult to reconcile with Rust's compilation model, but even C++ has higher-kinded types! (Haskell 98 is also closer to that extreme, apart from a couple of things like let generalization and polymorphic recursion.) |
This comment has been minimized.
This comment has been minimized.
I was suggesting that the type checker never introduce polymorphism on the user's behalf. What's wrong with that? It would exclude all but the first choice.
The example I gave isn't first-class polymorphism; it is plain old boring prenex polymorphism. First-class polymorphism (albeit only rank-2) would be an example like this: fn main() {
let f = |g| {
g(0u);
g(0i);
};
let g = |a| a;
f(g);
}which also doesn't work in Rust. |
This comment has been minimized.
This comment has been minimized.
Ah, okay. I've thought of that as well. It seems like a reasonable idea, I don't know if anyone's done it before. But as far as using type lambdas to bridge the gap between default type parameters and HKTs is concerned, it doesn't help at all. If the presence of default type parameters on common types forces impls of HKT traits for those types to be written for type lambdas, and type lamdbas are never inferred, then you're back in the same place, which is that inference doesn't work when you want it to.
Yes, I was imprecise, sorry. They are connected in that whether, when, and how to infer polymorphism is also the big issue in systems with first-class polymorphism, and let generalization is an instance of inferring polymorphism (even if first-rank), so I feel that these things are along a spectrum. |
This comment has been minimized.
This comment has been minimized.
From what I read on the Internet, it appears to be what Scala does.
The problem you posed above with struct Pair<A>(A, A)
struct WrapInt<W> { wrapped: W<int> }
trait Confused { fn bar(&self) }
impl<type<type> W> Confused for WrapInt<W<Pair>> { ... }
let foo = WrapInt { wrapped: Pair(Pair(0i, 0i), Pair(0i, 0i)) };
foo.bar();What should This combined with the fact that checking for instance overlap seemingly requires higher-order unification is probably a sign that type lambdas are not compatible with language features that attempt to infer terms from types with any sort of coherence. |
mitsuhiko
referenced this pull request
Oct 14, 2014
Merged
First-class error handling with `?` and `catch` #243
alexcrichton
force-pushed the
rust-lang:master
branch
from
b9e2b8c
to
5020131
Oct 29, 2014
aturon
referenced this pull request
Nov 21, 2014
Closed
libs: generalize `as_ref`/`by_ref` adapters to use `BorrowFrom` #19188
This comment has been minimized.
This comment has been minimized.
|
@aturon and I had a pretty detailed discussion about default type On inferenceThe monad exampleLet me begin by translating the example that @glaebhoerl pointed out into theoretical Rust syntax:
Here we wind up with a (higher-kinded) type variable There is also a trait obligation that This implies that to write an example like that in Rust, presuming we don't have curried partial application, would require a type annotation. For example, using UFCS, one could write A struct exampleHowever, let's poke a bit further into other examples. Here the idea is to drill into things we actually expect to use HKT for in Rust. One such thing would be the ability to reason independently about pointer types. For example, I might want to write:
Now I would like to write:
and I would like Rust to infer that the type of There is however an interesting compromise. We might restrict type lambadas to always be a partially applied type, though not necessarily a curried one. In other words, a value of kind Note that we still cannot infer the In a more complex case, we might therefore require annotations:
Problems with curried partial type applicationAt some point I made a statement that curried partial type application a la Haskell is not very "Rusty". What I meant mostly is that we don't do currying in general (i.e., not for ordinary functions), so it seems Based on the syntax alone, Other anticipated uses for HKTThe other major use for HKT that we anticipate is on associated types. For example, a trait Iterable {
type Elem;
type Iterator<'a>;
fn iter<'a>(&'a self) -> Iterator<'a>;
}In cases like these, the limitations on inference don't apply at all, because we are propagating forward rather than backward (that is, we don't have to deduce the function from its output, as in the other examples). ConclusionIn conclusion, it seems like default type parameters have a lot to offer in terms of convenience and have proven very useful. They do interact poorly with curried partial type application a la Haskell. However, curried partial type application is a poor fit for the Rust due to the kind of |
P1start
referenced this pull request
Dec 17, 2014
Closed
rustc: inference for numeric literals does not take default type parameters into account #15760
This comment has been minimized.
This comment has been minimized.
|
Is this something that will make it into 1.0? |
This comment has been minimized.
This comment has been minimized.
|
@mitsuhiko I would expect this to happen before 1.0 final, but not necessarily for the alpha in two weeks. |
nikomatsakis
referenced this pull request
Jan 31, 2015
Merged
Infer whether a closure implements `Fn`, `FnMut`, etc based on what actions it takes #21805
bors
added a commit
to rust-lang/rust
that referenced
this pull request
Feb 1, 2015
nikomatsakis
referenced this pull request
Feb 2, 2015
Closed
Closure kind inference: infer whether a closure is `Fn`, `FnMut`, etc #16640
aturon
referenced this pull request
Feb 4, 2015
Closed
Tracking issue for Finalize defaulted type parameters (RFC 213) #21939
aturon
merged commit 1662214
into
rust-lang:master
Feb 4, 2015
This comment has been minimized.
This comment has been minimized.
|
After some fairly extensive discussion on this RFC, the core team is convinced that this feature will pose no serious problems for type inference around a future HKT extension. See this comment for details. Other than those concerns, this feature is a frequently-requested one, and a natural extension given integer fallback. I have merged the RFC; the tracking issue is here. |
nikomatsakis commentedAug 26, 2014
This RFC proposes finalizing the design of defaulted type parameters with two changes:
_to explicitly use a defaultRendered view.