5 ms·
Wouldn't this be true of all statically typed languages that don't allow type inference in function signatures?
by nu11ptr 4y ago
Wouldn't this be true of all statically typed languages that don't allow type inference in function signatures?
- steveklabnik 4y agoI do not claim this property is unique to Rust, it's true. I think it's important to Rust conceptually because the type system is one of the ways in which Rust achieves its most important goals, but that doesn't mean that it's exclusive to Rust or unimportant elsewhere. I do find that lifetimes cause issues here that don't happen in, say, GC'd yet statically typed languages. Rust puts things in the signature that aren't there in many other places. I think the accessor/mutator example is so common when talking about this problem precisely because it's a pattern that's pretty normal in other statically typed languages, but doesn't cause any issues there, thanks to GC and/or lack of explicit lifetime tracking.
- nu11ptr 4y agoI do think this is a nice thing in general and one thing I find annoying in Ocaml without an interface file
- c-cube 4y agoYou can write explicit types in OCaml, it's actually quite smooth. Over the years my style has evolved towards that and now I almost always write at least the return type when I write a function!
- jerf 4y ago"Rust puts things in the signature that aren't there in many other places." I expect this sort of thing scales up as you are putting more in the type signature. Simple manifestly-typed languages like C or Go can't say much in their type signature compared to Rust, so it is harder to notice as a principle, though I think it's still present if you examine it hard enough. (A good example of how learning another language can be the best way to advance in your "core" language.) Haskell is an interesting supporting case for your core thesis; it can put a lot more in its type signature than most languages, but it also has inference, even pretty good inference considering how strong and complex the type system is. However, community practice is (and has been for a long time) to put type annotations on all your top-level functions, to a large degree for the same reason you are talking about. Haskell can get away without annotations to a large degree, but you'll be chugging along changing this & tweaking that, and suddenly your entire program explodes into a mess of type error messages attributed to all your code everywhere because of some change you made. Explicit annotations contain the blast radius of such changes and keep the compiler messages pointing at something much closer to the vicinity of what you actually changed and somehow broke. At the top expression level, type inference is treated more as a tool for determining the top-level types than a solution on its own terms.
- FullyFunctional 4y ago100% In fact much of "debugging" when I was writing Haskell was to increasingly hand type-annotate stuff to catch where my assumptions were flawed. Definitely, top-level exported entries almost all get an explicit signature is it's part of the documentation. Rust's level of type inference feels very natural to me and I don't miss what additional inference Haskell provided.
- dllthomas 4y ago> Rust's level of type inference feels very natural to me and I don't miss what additional inference Haskell provided. The biggest place I miss Haskell's inference is the repl; When futzing with a new library I very much appreciate being able to glue things together in ghci and ask what the type is. I don't know how to get something quite that general quite that easy in rust, although 'let _: () = my(expression)` is often good enough.
- FullyFunctional 4y agoI again agree completely, especially when learning, but even today I will occasionally prototype stuff in ghci. There is irust which seems to work well. I haven't used it enough find the limitation (eg. can you define new types at the prompt? I haven't tried).
- steveklabnik 4y ago> I expect this sort of thing scales up as you are putting more in the type signature. Yep, I would agree with this. On my mental list of "stuff to blog about someday" is a post along these lines. For me, most Rust code fits into a sort of sweet spot, with enough types to make things helpful, but not so many that it causes pain. Other people have different preferences and/or goals when it comes to these sorts of things. Incidentally, I was porting some Haskell code to Rust the other day, and ran into some of these differences. Oops, there's a lot more to this signature than I originally considered! This caused me to completely change the API of the code, given that what's natural for Haskell isn't always natural for Rust.
- 4y ago
- cmrdporcupine 4y agoI was confused by this, too. It feels like the target audience for this post must be people coming from dynamic-typed, late-bound languages? Hence the references to e.g. Ruby? Certainly Rust's immediate mainstream "competition" all have the same constraints. However, languages in the ML language family like StandardML and OCaml and Haskell do get away (sometimes) without explicit type signatures in function definitions. But I believe it's also considered good practice to declare them explicitly much of the time anyways. Because the alternative is, as the article pokes at, confusing error messages.
- steveklabnik 4y ago> It feels like the target audience for this post must be people coming from dynamic-typed, late-bound languages? You're picking up on something, for sure. When I originally put metaphorical pen to metaphorical paper, I intended to talk about static types vs TDD. Then this background got a bit long, so I decided to just make it its own thing, with that topic to follow. You're right on the "considered good practice" aspect, which I didn't call out specifically because I generally try not to frame things as "Rust good, other language bad," hence the "Rust vs what Rust would be with function-level inference."
- marcosdumay 4y ago> But I believe it's also considered good practice to declare them explicitly much of the time anyways. Much of the time is very different from always. For example, Haskell would be incredibly restrictive if every time you named a lambda you had to declare its type. People don't even declare most of their functions, just the top level ones. Always declaring the types would lead to large functions that are less general... what is common in Rust. (But that's not the only reason they are common in Rust.)
- dllthomas 4y agoTo be fair, you don't need to declare the types of your lambdas in Rust, either.
- 4y ago