Earlier quoted context omitted.
> That also explains why TDD is more popular in say Ruby or Python vs. Java. I'd say that TDD being more popular in untyped languages speaks against TDD, as it hints that maybe some of its benefits are covered already by a type system.
types are just autoverified logic. tdd just tests logic which cannot be typed in given type system. in lean4 one can type a lot(dependant types to test integration shapes and proofs are proptests).
Isolating complexity is the essence of successful abstractions
51–60 of 87 posts
Re: Isolating complexity is the essence of successful abstractions
#52That's why Typescript/Python optional typing hit the best balance for me. Coding in duck-typed language is generally fine when your test suite is as fast and frequent as a type checker. That also explains why TDD is more popular in say Ruby or Python vs. Java. Speaking of Java, the problem with types is when you try to reify every single problem you encounter in your codebase. By the way, python has structured types…
> That also explains why TDD is more popular in say Ruby or Python vs. Java. I'd say that TDD being more popular in untyped languages speaks against TDD, as it hints that maybe some of its benefits are covered already by a type system.
If you look at any well tested program in a dynamic language, almost all the tests check the same properties that a type system would also check by default. If you remove those, usually only a few remain that test non-trivial properties.
EDIT: And I just love that in the time I took to write this, somebody wrote a comment about how it isn't so. No, it is still blatantly obvious.
Re: Isolating complexity is the essence of successful abstractions
#53I don’t think I agree that either typescript nor rust successfully hide the complexity in their type systems. By the nature of type systems, they are tightly coupled with the code written around them. Rust has rich features to handle this coupling (traits and derives), but typescript does not.
It's not about hiding the complexity in the type system, that is, the complexity of the type system. At least for Rust, it's about that (yes, complex) type system isolating the even worse complexity of tracking lifetimes and aliasing and such, for all possible control flow paths, in your head. It's harder to summarize what Typescript is isolating, except that JavaScript function signatures are the flipping wild west…
For example, `Object.assign` overrides all property with same name. Sometimes you use it to construct a new object, so it is a safe usage. But what about using it to override the buildin object's property? It is definitely going to explode the whole program. However there isn't really a mechanism for typescript to differ the usage is safe or not. So in order to maintain compatibility, typescript just allow both of them.
And typescript in my opinion don't really isolate very much complexity. But it does document what the 'complexity' is. So you can offload your memory tax to it. Put it away, do something else, and resume later by looking at what definition you write before. In this way. It can make managing a big project much easier if you make proper use of it.
Re: Isolating complexity is the essence of successful abstractions
#54Earlier quoted context omitted.
You were going to have to deal with that complexity either way. Now it's expressed somewhere, and if you craft it right, enforced so it's harder to get things wrong. https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...
This view has always been bullshit. It doesn't differentiate between the complexity of the types themselves and the complexity of representing them in a static type system.
The main problem is that a lot of developers don't know how to use the type system well, so they write code in a way that doesn't take advantage of the type system. Or they just write bad code in general that makes life difficult despite a type system.
It doesn't solve all problems, but if you use it well it can solve a lot of problems very elegantly.
Re: Isolating complexity is the essence of successful abstractions
#55Earlier quoted context omitted.
> However, when I was a kid a would put a firecracker next to an object. I didn't bother running the scenario through a compiler to see if the object was of type Explodable() and had an explode() method that would be called. Duck typing: if it quacks like a duck, and it explodes objects next to it, it's a firequacker
Duck typing. If it quacks like a duck and swims like a duck it might be a duck. But it might also be a nuclear submarine doing a duck impersonation. The question is whether you want a nuclear submarine in your pond.
The problems are that you won't remember to do the same exact checks everywhere and document them.
Re: Isolating complexity is the essence of successful abstractions
#56Earlier quoted context omitted.
Id say if you think tests and types are doing the same thing in the same way you are badly abusing at least one of them. One attacks the problem of bugs from the bottom up and the other from the top down. They both have diminishing returns on investment the closer they get to overlapping on covering the same types of bug. The haskell bros who think tests dont do anything useful because "a good type system covers all…
> The haskell bros who think tests dont do anything useful because "a good type system covers all bugs" themselves havent really delivered anything useful. Please don't do this. It's not constructive.
Re: Isolating complexity is the essence of successful abstractions
#57In slightly different words, an abstraction separates what client code needs to reason about from what it should be able to ignore. Of course, if an abstraction isolates client code from certain complexities, that will contribute to the success of the abstraction. But it’s not the essence of what an abstraction does, or a necessary condition for it to count as successful.
Re: Isolating complexity is the essence of successful abstractions
#58Earlier quoted context omitted.
When I do TDD (virtually every time i write a line of code) each test scenario isnt just a way to verify that the code is working, it's also a specification - often for a previously unconsidered edge case. Throwing away the test means throwing away that user story and the value that comes with it.
I believe you (other than tests being specifications, they are examples at best). But that doesn't change the fact that TDD looks more adopted in untyped languages, and that deserves an explanation. Mine is that a lot of potential errors (typos, type mismatches) don't need to be exercised by running code in typed language. Yours is... well, you don't really address it.
If you're not, that suggests you're not doing them right which in turn suggests why you might have an issue with them...
Re: Isolating complexity is the essence of successful abstractions
#59Let's take the example of memory management: by pushing that complexity into the type system, Rust forces the programmer to deal with it and design around it. At the expense of some performance, we could instead push this complexity into a runtime garbage collection system. Since the runtime system understands things about the runtime characteristics of the program that can't be proven via static analysis, it can also handle more things without the programmer having to intervene, thus reducing the difficulty of the programming language. For most programmers this is a positive tradeoff (since most programmers are not writing software where every microsecond matters).
Similar tradeoffs exist in many different areas of software engineering. One monolith, where all the information is in one place, is easier to write code in than two microservices, which keep having to ask each other questions via API call. Yet, sometimes we need microservices. Rendering your web application entirely on the frontend in React, or entirely on the backend with templates, where all the logic lives in one place, is much easier than doing server-sided rendering then hydrating on the frontend. Yet, sometimes we need server-sided rendering and hydration.
Complexity is an irreducible constant, yes, but cognitive load is not. Cognitive load can increase or decrease depending on where you choose to push your complexity.
Re: Isolating complexity is the essence of successful abstractions
#60Earlier quoted context omitted.
types are just autoverified logic. tdd just tests logic which cannot be typed in given type system. in lean4 one can type a lot(dependant types to test integration shapes and proofs are proptests).
TDD also asserts that if you make a change you don't break anything. Most programs are too complex to keep all behavior in your head so sometimes what looks like an obvious change breaks something you forgot about. Types won't tell you because you adjusted the types, but the test will tell you. (if you have the right tests of functionality - a very hard problem outside the scope of this discussion)
Now, proofs can be often devilishly hard to write whereas tests are easy (because they're just examples), so in practice, types probably won't supplant tests even in dependently typed languages.