5 ms·
I'm torn on this. I prefer explicit types in declarations as well, but very much like the convenience of inferred types. I find myself thinking, though, that th
by sidlls 9y ago
I'm torn on this. I prefer explicit types in declarations as well, but very much like the convenience of inferred types. I find myself thinking, though, that the demand for the convenience of inferred types in these languages is a result of the verbosity in the type specifications themselves, usually with generics (Rust) or templates (C++). I rarely use the convenience of inferred types for concise type labels (e.g. non-generic structs, integers, etc.). Maybe this is a deficiency in the languages' designs?
- steveklabnik 9y agoWe put a lot of consideration into this. Basically, we could let you elide types everywhere, but didn't. There's a few reasons why, and they're sorta inter-related. If you let type signatures be inferred, then you can get very poor errors. This happens when you change some code in the body, that changes the type signature of the function, which then causes an error to happen where the function is used. This misdirects from the source of the error, rather than helping you figure out what goes wrong. Conceptually, signatures are the place where contracts are enforced, especially in Rust. Everything about a function should be expressed in its signature, so that way we can do analysis based on only the signatures. This helps in a few different ways: it's faster for the compiler, and it's also easier for the human reading it. The bodies of functions, however, aren't exposed to the greater world. With a defined signature, the compiler can give you great error messages, pointing exactly to where you made a mistake. In this case, inference makes sense. It's not just about convenience though. You can see this in languages like C++, which optionally support type deduction (but not full inference): auto a = 1 + 2; int a = 1 + 2; With auto, the type is deduced. Without auto, you specify the type. There are various arguments that range between "never auto unless you have to" and "always auto". Consider this example https://stackoverflow.com/questions/6900459/the-new-keyword-auto-when-should-it-be-used-to-declare-a-variable-type https://stackoverflow.com/questions/6900459/the-new-keyword-... SomeType<OtherType>::SomeOtherType * pObject = new SomeType<OtherType>::SomeOtherType(); auto pObject = new SomeType<OtherType>::SomeOtherType(); Here, auto improves readability, because you end up just repeating the type over again. This was my main frustration with Java's generics back in the day. (I primarily used Java in the 1.4 ~ 1.5 era, I know today's is a very different beast!) This is directly related to the parent post; some might say "explicit is better than implicit" but in my opinion, while that's often true, implicit is better than explicit sometimes. The above is a great example. The post is all about trying to tease out better words to talk about these differences.
- gravypod 9y agoI think there is a lot of useful information, within a function body, that can be created by inclusion of specific types. I don't think your decision was bad. People obviously like the language and the compiler's type inference systems are apparently extremely advanced. In my opinion this is an anti-pattern for beginning developers and complicates the development of IDEs. This is for a number of reasons. 1. Saving screen space is not a good reason to omit types Long long ago the people typing away at Bell Labs made these same decisions. All across the UNIX kernel and user space they used partial/shortened names. This did not help users nor did it help developers. K.T. is said to have joked that the missing "e" from "ceat" is his biggest regret from the UNIX system. We don't use 80x40 terminal displays and most of us type at faster than 25wpm. The space or time saved when writing... let people = new List<String>(); When compared to... List<String> people = new List<String>(); Is not worth it in my opinion. This is even more so when you have situations like this List<String> people = getGuestList(); vs let people = getGuestList(); In my opinion this is considerably more difficult to understand what type I'm working with. I have to now go and read the header for `getGuestList()` to find out what I can do with my `people` variable. 2. IDE development will be more difficult. This is minor, and will eventually be a solved problem, but writing an IDE that doesn't have immediate type information for everything in scope will be more complicated. Some may even say this might be impossible to accomplish via static analysis. Because of this the development of things like the RLS are much more difficult. 3. The seasoned but new developer will struggle to find how things work. When I pick up a new programming language at this stage in my career I no longer read through a book or documentation. I sit down and I try and accomplish a project. I find a task, find some code that does the subsets of the problems I want to solve, read that code, then implement my own solutions with the knowledge that I've gained. With Rust this approach doesn't work (for me). I have not been able to pick up Rust. I don't know what kind of objects are being used to hold which kind of data without reading almost every single line of code in the program (or at least everything accessible from the scope I am interested in). In Java, C, etc I get some clue as to what's going on. In Java I need to only read maybe the first few words on every line to get an idea of what will happen or what is being used. public boolean isOnGuestList(String person) { List<String> people = ...; List<String> plusOnes = ...; return people.contains(person) || plusOnes.contains(person); } I might not know what a List<String> is but I can easily find out. This biggest effect this has is that since I have been programming for a long time I have an intuition where certain language features will be used. Every* language has a KV store, a list, some way to interact with file descriptors, etc. I know what kinds of programs will do those things and I can look at those programs for reference. Unfortunately for languages like Rust I need to already know the general types that people use to understand the code. All I would see is... fn isOnGuestList(person: string) -> string { let people = ...; let plusOnes = ...; people.contains(person) || plusOnes.contains(person); }