7 ms·
Types Are Anti-Modular
- baguasquirrel 15y agoExistential types? The author alludes to this, and then proceeds in ignorance of what he'd just said for the rest of the post. In Java, all you have for these is interfaces, so it's no wonder people think that types are problematic. Types aren't the problem. Crappy types are the problem. Not making the types lightweight enough that people can dish them out at their pleasure, that's the problem. When the math don't work well enough, make better maths.
- swannodette 15y agoExcept you can't unpack existential types, right? http://stackoverflow.com/questions/2300275/how-to-unpack-a-haskell-existential-type http://stackoverflow.com/questions/2300275/how-to-unpack-a-h...
- dons 15y agoUnpack in this sense means "unbox", and you can specialize before you hide the type.
- prodigal_erik 15y agoIf g returns an x and f takes an x, I can do f(g()) without knowing anything else about type x. Even type inference will choose an x that makes g compatible with f, or complain if there isn't any. So the system is still modular at the source level, which is important because it keeps the project comprehensible. What you don't have is binary-level modularity, but that's just an efficiency hack that dates back to when computers were almost too slow to run compilers. Now it should only matter to people who obfuscate by refusing to ship source (or source-equivalent intermediate files, which at least one Ada and one C++ implementation used to handle separate compilation of polymorphic code) and living with the lack of whole-program optimization.
- alok-g 15y agoCopy-pasting a comment on the original article here, since I have the same comment/question: Brian said... Dynamic typing doesn't restore the modularity- it simply delays checking for violations. Say module X depends upon module Y having function foo- this is a statement which is independent of static vr.s dynamic checking. This means that that modularity is lost- you can't have a module Y which doesn't have a function foo. If module Y doesn't have a function foo, the program is going to fail, the only questions are when and how. What I don't get is why detecting the error early is a disadvantage. It's well known that the earlier a bug is detected, the cheaper it is to fix. And yet, all the arguments in favor of dynamic typing are about delaying the detection of bugs- implicitly increasing their cost. 6/05/2011 6:49 AM
- 6ren 15y agoThe article's just claiming that the modules of dynamic languages can be compiled independently; not that it will be correct. Just my thoughts: There's a bigger question here about types, though. I think the argument is that the ceremony of types gets in the way more than it helps, and it requires up-front design. Many people like dynamic languages for their flexibility; and when you get bugs, you have to fix them anyway. There's an assumption in typed languages that we can design our types well enough upfront to help solve the problem. It's interesting how types create a dependency on the interface (i.e. if you use a type, you depend on it). One of the ideas of Abstract Data Types was to reduce dependency on the internal implementation details; but you're still dependent on the external interface. If you change the interface, it creates problems (e.g. unit testing claims to give you confidence to refactor, but if you change interfaces, you also have to change the tests...). EDIT added "claims to" This seems intrinsic to modularity, and I can't really see any solution to this; except for ways to make it easier to cope with interface change. Some insights may come from webapp API's, where the dependency is more explicit (you have to send and receive serialized messages).
- thurn 15y agoWeb APIs often have explicit versioning. Perhaps that approach could be adopted for programming, where you have the option to specify the version of a function you are using. This would enable gradual migration and adoption of new features.
- dons 15y agoFrom the Haskell reddit discussion: > It's a simple truth, indeed a tautology, that if you want the compiler to check the consistency of interactions between modules, then the compiler must know the information that it is checking. That's all that's being said here. It really shouldn't surprise anyone. If you dispose of typing, then someone still needs to know that relevant information in order to write correct code; but it is the programmer, rather than the compiler. or, with more wit, > Types are anti-modularity in the same sense stop signs are anti-transportation. http://www.reddit.com/r/haskell/comments/hs39c/gilad_bracha_types_are_antimodular/ http://www.reddit.com/r/haskell/comments/hs39c/gilad_bracha_...
- swannodette 15y agoThe blog post isn't about correctness - it's about modularity. Lispers and Smalltalkers are used to programming with very small compilation units - individual methods and individual functions. Being able to fix running programs w/o going through the whole compile cycle is a demonstrable productivity gain. Types and modularity are at odds. The antagonism is readily apparent in languages that support dynamic and static typing - Qi. It's RCEPL (Read-Check-Eval-Print-Loop) has serious restrictions in comparison to its standard REPL.
- jamii 15y agoI suppose turning type errors into type warnings would satisfy both camps. That way you can experiment with incremental changes and then fix up the rest of the program when you are ready. Erlang + dialyzer works quite nicely like this.
- dons 15y ago> Being able to fix running programs w/o going through the whole compile cycle is a demonstrable productivity gain. Indeed! Removing many sources of runtime failures at compile time is also a demonstrable productivity gain. So good thing that static typing is no barrier to dynamic extension or modification -- however, you must defer type checking of splice points between components until later stages. (This was my PhD thesis).
- ankrgyl 15y agoThis article confuses the reader with a weird implicit definition of modularity. The miscommunication is rooted here: > Separate compilation allows you to compile parts of a program separately from other parts. I would say it was a necessary, but not sufficient, requirement for modularity. The author goes on to assert that a requirement for modularity is that modules that depend on each other should be able to be compiled independently, without even a specification to glue them together. This just doesn't make sense. This might be a debatable point, but it has nothing to do with modularity.
- hxa7241 15y agoLack of types gives the illusion of more modularity. As was mentioned -- there are still dependencies. The only difference is that, without types, checking whether the parts fit together is deferred until later -- but it will still be done. (All parts of software have to be connected, and all have to be consistent.) And therefore you could argue that types are pro modular: because they allow you to write separate pieces without needing the whole to test if it is OK -- which you cannot do without types.
- colanderman 15y agoUm... NO. This argument applies equally to functions and constants and is equally wrong. Types are not anti-modular: the culprit is module systems that don't allow type parameterization. Systems such as Java solve this problem for functions and constants by allowing code to be parameterized over interfaces. Bam, you can compile code using said functions and constants separately from their implementation. The solution for types is exactly the same: move types into interfaces. Guess what, Objective Caml and Coq do this via module functors and it's glorious. If my airline scheduler module needs to compile separately from my graph module, well I can write: module Scheduler(Graph: sig type graph val traverse: graph -> (string -> unit) -> unit end) = struct ...blabla... end and I can compile that code without ever writing a snippet of the graph module -- not even defining the ADT! TLDR: this problem has been solved 30 years ago by people who actually use typed languages.
- vog 15y agoThanks for pointing this out. I was about to believe the post was correct, but it seems it isn't. However, just to be sure I understand you correctly: Is the module functor compiled only once, or once for every parameter type? I'm asking because if it's compiled only once, the generated code might be significantly less optimized. This might not play a role for complex parameter types, but could miss lots of good optimization opportunities for simple types such as int or char. How is this solved in those languages? Also, is it just OCaml and Coq, or is this possible with other strongly typed languages (such as Haskell) as well?
- Chirono 15y agoHaskell's module system isn't very advanced. In fact, it about as simple as it could get. This is pretty well known known and there are proposals floating around to add more advanced features. But there generally isn't much need and, as you mention, adding things like module type parameters prevents many cross-module optimizations (which GHC does quite aggressively). In practice, it doesn't seem to cause much of an issue, but this may just be down to how Haskell tends to be used.
- 15y ago
- dkarl 15y agoI disagreed strongly with this post and was about to post a rebuttal, but then I realized there actually is a situation where untyped languages enjoy a concrete, practical advantage because of the lack of compilation dependencies. That situation is when you are writing a module (something that would be a compilation unit in a typed language) that calls missing or nonexistent library interfaces, and you want to test parts of the module that do not depend on the missing APIs. In a typed language, you can't do it. You would have to satisfy the dependencies first so your code would compile, or you would have to copy snippets of your code out of the compilation unit into a REPL or another file so they could be compiled separately. In an untyped language, you can experiment with parts of the module and even start writing tests without having to make sure all of the module's dependencies are satisfied. Aside from that one advantage, though, it's the same with types or without them. For a module as a whole, the compilation dependencies that exist in a typed language are the same as the runtime dependencies that exist in an untyped language. Whether you're using a typed or untyped language, you can write whatever code you want, but you can't do anything with it until you satisfy its dependencies. With that one exception, of course: in an untyped language, you can run code that doesn't depend on the missing APIs but happens to be in the same module as code that does depend on them. Overall, I don't think that amounts to much, especially compared to a typed language like Scala where you can copy snippets into a REPL. Or even better, Ensime is an Emacs mode that is supposed to let you work with Scala the way Slime lets you work with Common Lisp. (I haven't tried it myself yet, but it exists, and people are using it for real work.)
- msie 15y agoYeah I really hated this post. Starts off with a flame-baiting title. Then resorts to very qualified statements to support it. After reading it I'm wondering if it matters at all and I'm feeling dumb all throughout the discussion. :-( Update: Aarghh, it's just aggravating to read an academic stirring up the pot over some point of theoretical purity. And I love theory too. ;-)
- dkarl 15y agoIt's the worst kind of theory: a theoretical point presented as a practical insight without any explanation of what the practical ramifications actually are.
- riobard 15y agoWhy not structural typing?