3 ms·
> 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 w
by 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 agoSo the issue you faced was that you used z twice, and x not at all? In that case, linear types (http://edsko.net/2017/01/08/linearity-in-haskell/ http://edsko.net/2017/01/08/linearity-in-haskell/) may have caught that.
- kazinator 10y agox not being used at all can be caught by a simple unused variable warning. In this situation, the variable's value was used somewhere, so that didn't go off.
- codygman 10y agoIf you used newtypes for z and x it would have been caught.
- pka 10y agoHave you tried what I'm talking about, i.e. prototyping something by specifying the types and iterating on the core design first before implementing large chunks of the required functionality? It's probably hard to see the benefits without having tried it first. This [0] is one of the bigger Clojure projects I've done (around 2.5kloc), also without spec/schema, and I really didn't dare refactor much, even when having a clear understanding of how things could be done better. It was just too much work. With types I'd go through 10 design iterations before settling for something I'm satisfied it, and even halfway through the project changing things radically isn't a problem (I've worked on a 50+ kloc Haskell backend service, and changing core data structures used pervasively throughout the codebase was a 10min job, literally.) [0] https://github.com/pkamenarsky/tellme https://github.com/pkamenarsky/tellme
- abc_lisper 10y agoI see what you are trying to say. You are saying Clojure is not the right tool to do top-down design. I agree with it. It is however a very good tool to do bottom up design. See https://www.youtube.com/watch?v=Tb823aqgX_0 https://www.youtube.com/watch?v=Tb823aqgX_0
- pka 10y agoI don't think this is top-dow design really, just an overengineered OOP monstrosity. Top-down doesn't mean that you have to model the universe first before getting to model your problem :)
- codygman 10y ago> 1. Types don't enforce meaning Why do you think this? > Haskell style types only work as far as they enforce meaning. Doesn't this contradict your statement above or are you saying Haskell style types never work because they don't enforce meaning? getUser :: IO (Maybe User) The above function enforces that getting a user can fail and you must contact the outside world to get a user.
- abc_lisper 10y agoAlright.. What I meant to say was, Types don't enforce all meaning ie., the enforcement of contracts through types go only as far as they mean something to the problem you are applying it to. It does not cover all the complexities of the problem, or the way the code is changed in the future. EDIT: This is also why it is easy (and nice) to implement parsers in strictly typed functional languages, because parsers are well studied theoretically. The problems in the real world are not studied well enough for contracts enforced via types to work completely.
- dllthomas 10y agoTypes don't enforce meaning, types are a tool I use to enforce consistency of meaning along certain important dimensions. I actually find that even more important when the situation is messy, because I'm likely to initially mischaracterize some aspects of it initially and when I go to change things it's very useful to be told what's now inconsistent.
- abc_lisper 10y agoI get what you are saying. What I'm trying to say is typing takes too much from me, in terms of complexity over-head, that I'm better without. I found this is true in practice now. As I said before, I write tests to do what you say types do - for me, that is enforcing meaning. Types do allow for easy refactoring, and I think that is weakness for untyped languages.
- dllthomas 10y ago> The above function enforces that getting a user can fail and you must contact the outside world to get a user. That seems sort of backwards. It enforces that the caller be able to handle failure (and similar for IO). It may well be that "getting a user" doesn't do either (e.g. `pure (pure defaultUser)`)