5 ms·
> The downside that the author points out seem to be often neglected. Constant interruption and battling with the type system during development, when mental ov
by loup-vaillant 8y ago
> The downside that the author points out seem to be often neglected. Constant interruption and battling with the type system during development, when mental overhead is a problem.
Dunno about Rust specifically, but I have written a little Haskell, and a significant amount of OCaml. I found that the type system reduces my cognitive overhead, compared to looser languages like Python or Lua. The compiler is disciplined, so I don't have to be.
In practice, with few exceptions, when the type system is refusing to compile something I just screwed up, and I'd better fix my mistake right away. I'd wager that with few exceptions, when Rust is refusing to compile something, you are making a mistake that in C++ would have bitten you down the line.
What you call "fighting the type system", I call "fixing bugs".
- blattimwind 8y ago> you are making a mistake that in C++ would have bitten you down the line. Taking some C++ code from an explorative phase that "usually works kinda ok" to something seaworthy requires, in my experience, a huge amount of work (which usually neglects a few issues that will bite you later regardless). I'm not so sure the (probably) faster exploration in C++ compensates for this compared to more "strict" languages like Rust. Of course, if the functional requirement is only "usually works kinda ok", then you don't really need something like Rust.
- loup-vaillant 8y agoI found that strict compile time checks speed up my exploratory phase. That's because the type system provide an even tighter feedback loop than a REPL, and effectively cuts down the search space. Other people seem to have experienced the opposite, though. I have no idea why.
- milesvp 8y agoIt might depend on the problem space. I dunno. But I suspect it depends a lot on the type system and type inferencer of the language you use. It is easier to write a true statement than it is to prove the statement is true, and getting a compiler to agree to compile quite literally requires proving a statement is true. This can be more or less difficult based on the language. I’ve read a lot of war stories, particularly in java, about having to do a lot of crazy convoluted code to get the compiler to realize that yes actually these 2 objects are interchangeable from an API/Interface point of view. I don’t know, but I suspect this may be a lot less common in languanges using hindley-milner type system, since that lends itself to very good type inferencing.
- slavik81 8y agoTo me, the question is whether the time spent dealing with compiler errors is due to fixing real problems that the compiler is pointing out, or whether that work is mostly reworking a correct program to satisfy the compiler when it cannot yet see that the program is correct.
- firethief 8y agoI don't think humans have anything major in their toolkit for reasoning about memory management / aliasing that the Rust compiler doesn't; when non-trivial code cannot be expressed in terms of single-ownership at any given point, code loses correctness-at-a-glance. There are cases where a safe Rust abstraction is built on unsafe code, including most of Rust's collections; the API contract of every stdlib collection is: the collection owns its elements. Interestingly, such a contract can generally be implemented safely, but would have different performance characteristics; e.g. doubly-linked List could use indexes into a Vec. This example mirrors a pattern seen in languages that discourage mutation: break the rules internally for performance optimizations, but maintain the invariants at API boundaries to contain the cognitive overhead. Even when Rust's rules need to be broken, they are useful to contain complexity.
- deleted 8y ago[deleted]
- mlthoughts2018 8y agoI worked professionally in Haskell for a long time and I cannot disagree with you more. This is always the promise of strict functional languages but in a business setting where the very nature of what you build has to be arbitrarily mutable at the whim of competing interests between stakeholders, engineers, customers, etc., it just doesn’t work. You end up with the same spaghetti code messes, unexpected runtime errors, indecipherable crashes, etc. no matter what degree of strict verification tooling you use. The introduction of lazy evaluation, difficult to debug tail recursion implications on memory consumption, elevating IO into the type system, burying details deep in type class patterns, relying on dozens of special case compiler directives to enhance Haskell... it becomes the same shit code mess as any other paradigm.
- tome 8y agoInteresting. I would be interested to know more details about what you worked on and with whom. This hasn't been my experience in any of the four jobs I've used Haskell at. I don't deny that Haskell leads to spaghetti code messes, unexpected runtime errors, and indecipherable crashes, but the Java that I've previously worked with and the Python I currently work with suffer from those to a degree greater by an order of magnitude.
- mlthoughts2018 8y agoIt was first for a bank, later for a data analytics start-up, and then also for an education technology company. I had many skilled functional programmers on my team too, including a few people who had done work on GHC itself. I’ve also see code messes in many languages. It’s a business culture problem, has nothing to do with the language or paradigm. I’ll greatly dispute with about Python though. It has been a language that facilitates notably cleaner and more extensible implementations in my experience. Seen messes in Python too, but absolutely no way it’s “an order of magnitude” worse. That’s simply ridiculous. The only way that’s happening is if the programmers are exceptionally bad to the extent that it would not matter which language they used.
- tome 8y ago
- asdkhadsj 8y agoCouldn't agree more. It was why my transition from Javascript (Node) to Go was very freeing - I had been so used to being constantly paranoid about my data types from Python and Javascript that even the most modest types of Go made a huge relief. Likewise, with Rust it is a relief after Go. In Go interfaces are a massive part of the language and they can only be pointers, which means much of your programming experience holds mental overhead needed to frequently answer "is this variable safe? Can I access it, or might it be nil?". There's definitely overhead in learning how to use the borrow checker, but I feel that's the same as any basic language features in any language. Once learned, I largely don't think of them, and I don't fear using variables or what is concurrently safe anymore.
- mntmoss 8y agoI can definitely see where the author was coming from in terms of new code. I've been of the opinion that it's probably better to greenfield new systems alongside a GC'd runtime(which makes Go a pretty good fit, but also D, Nim, etc. - and for the very experimental stages where the type layout is minimally understood - Python, Lua, JS, etc.), and to take on external dependencies to get a result working - and then rewrite parts with manual memory and custom libraries where the spec needs tightening. The main downside to this is that you're rewriting stuff, but the goal of the rewrite should be to express what is essentially the same functionality in more detail. Where I think the author might have erred is in switching between three different manual memory languages in the hopes of faster development. The algorithms needed for a 3D tool have a pretty high base complexity, making it hard to bootstrap the codebase - and I suspect that C++ will ultimately frustrate him just as much as Rust and C did before.
- olau 8y agoAh, but in this case you have already done the spec, otherwise the type system couldn't have checked it. With a more loose way of going about things, you don't have to write the spec. Spec'ing is hard, sometimes harder than actually solving the problem. I feel like someone is going to point out that spec'ing is a lifesaver in a certain situation, and I would agree. But perhaps not all situations.
- renox 8y agoThat is an oversimplification, very often you know something that cannot be expressed in the types, so escape hatch (cast/unsafe) are useful. That said, if you can wrap around these 'hack' in a 'sane' API, then it's OK if you canno/didn't the result is quite ugly..