5 ms·
> If I am facing a problem that I have never faced before, I like to start off without any types in my code, and then, as I understand the problem more, I like
by pka 10y ago
> If I am facing a problem that I have never faced before, I like to start off without any types in my code, and then, as I understand the problem more, I like to add in more contract-enforcement.
This seems to be a widespread sentiment.
In practice, I've found that prototyping anything remotely complex without types is so painful that I'd rather settle for an inferior design than trying to come up with the best possible abstraction.
In Haskell, I come up with a coherent skeleton without having to implement mundane details, hit a wall in the design space because of some case I didn't think of, come up with a better idea and then go back to the code and refactor with confidence. The type system always guarantees that my prototype is coherent as a whole. And I can do that dozens of times.
In Clojure, even with spec, I'd have to implement all my functions fully before being even able to test the design as a whole (sure, testing individual functions works fine in the REPL.) And after hitting a wall, having to reimplement everything every time is just too much work.
- Karrot_Kream 10y ago> In Haskell, I come up with a coherent skeleton without having to implement mundane details, hit a wall in the design space because of some case I didn't think of, come up with a better idea and then go back to the code and refactor with confidence. The type system always guarantees that my prototype is coherent as a whole. And I can do that dozens of times. Could you go into a bit of detail about this approach? Do you mock functions out and just specify type definitions, and fill in functionality as you go?
- tel 10y agoI'm about 5% into a side project implementing Erlang-like actors in Haskell and it provides an example of this workflow. https://github.com/tel/hotep https://github.com/tel/hotep Note that src/Hotep.hs exports a bunch of undefined functions. I've been able to verify that these types all make sense even without implementing a thing. As time goes on I may learn that the implementation drives the types somewhere else and then the compiler will make refactoring easy. However, already I've gone through about 5 iterations of this design which drove me to debug some structural questions about the whole affair and also dive deep into Erlang documentation to determine how they solved problems. These explorations and their results are encoded into the types. At this point I'm beginning to consider implementation and I can keep filling out just partial implementations against these types. I'll probably make 2 or 3 toy implementations to test out the ideas again with more strength before moving on the final ones. The whole time the types will be guiding that development and helping it move quickly. Key to this whole affair is the need to describe types utterly before a completely successful library is made... and also the ability to defer the burden of providing type-checking code for as long as desirable. Haskell supports this wonderfully—even more wonderfully with things like Typed Holes and Deferred Type Errors which enable a really great interactive experience I haven't yet needed to employ.
- abc_lisper 10y ago> In practice, I've found that prototyping anything remotely complex without types is so painful Just to offer a counter-point, I have a Clojure project here with 3k LOC, without using spec/schema. All I have is 700 LOC tests. The tests enforce semantic meaning, along with (some) contracts. I miss types from time to time, but it is no where near as bad as you mention. Against me is the fact is that my app is mostly self-contained, and written all by me. I am fairly certain the project would be atleast 30k LOC if I wrote it in Java. I think value of types only comes into being when there are many people working on a single code base. Repl/tests/integration tests will take one a long way before it reaches its limits. I don't buy the argument that types are useful for large codebases, because 1. Types don't enforce meaning. Meaning is far complex than type. Haskell style types only work as far as they enforce meaning. 2. Types have limits too. More accurately, humans have limits. Working with large codebases where there are 1000s of types is hard; which it shouldn't be because that was type systems selling point all along. Cleaner abstractions of code, separated by strongly enforced protocols(types) is the way to go, I think.
- kazinator 10y agoCannot proto-type without type; how ironically circular. :)
- kazinator 10y agoToday I caught a bug in a macro-expanding code walker, where it was expanding the wrong form. The syntax being walked is (foo-special-operator x y z . rest). x and z are ordinary forms that need to be expanded; y is a destructuring pattern (irrelevant here). The walker was expanding z in the place of x: that is to say, expanding z twice, and using that as the expansion of both x and of z. That's simply due to a typo referring to the wrong variable. The type system would be of donkey all use here, because everything has the correct type, with or without the mistake. The code referred to the wrong thing of exactly the right type. A lot of code works with numerous variables or object elements that are all of the same type, and can be mixed up without a diagnostic.
- neilparikh 10y ago