4 ms·
> The difference compared to a (good) static type system is that a good [1] static type system does not measurably reduce expressiveness This is pseudo-jargon.
by rntz 10y ago
> The difference compared to a (good) static type system is that a good [1] static type system does not measurably reduce expressiveness
This is pseudo-jargon. What counts as a "measurable" reduction? Do you have any substantive evidence that "good" typesystems don't reduce it? And why are only type systems which satisfy this property "good"? You talk cogently about tradeoffs later on, but this sentence taken literally suggests only a type system which doesn't make this particular tradeoff can be "good". Why?
My go-to languages for the past few years have been Haskell and Racket. There have definitely been times I picked Racket because I knew Haskell's type system would interfere with my expressiveness. Certainly Haskell is less restrictive than Rust with regards to ownership, but all type systems restrict you. I think GP is spot-on in pointing out that you just get used to it over time as you learn to think and code in ways that avoid pushing against the type-system's weak spots.
> But where garbage collection is an option, the tradeoffs that Rust makes become much less attractive.
Yes, precisely. It's not a fundamentally different issue from getting used to other static type-systems; it's just a different tradeoff.
- rbehrends 10y ago> This is pseudo-jargon. How do you measure expressiveness? Actually, not jargon. I had Matthias Felleisen's characterization of expressiveness [1] in mind (incidentally, he's also one of Racket's authors). > Do you have any substantive evidence that "good" typesystems don't reduce it? If we want to be precise, there is a continuum from statically to dynamically typed language. A dynamically typed language is, after all, the same as a static language with just a single polymorphic type. This means that we can trivially translate a dynamically typed program into a statically typed program, though obviously we don't gain anything by that at this point (this is essentially an argument that Bob Harper has advanced before) and we pay for it in additional verbosity. However, in practice we know that we don't need all that flexibility and can constrain types more, allowing us to get better type guarantees; static types then also allow us to do things that we can't do in a dynamic language (such as compile-time overloading). [1] http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.51.4656 http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.51.4...
- rntz 10y agoEDIT: I've been chewing over the following: > This means that we can trivially translate a dynamically typed program into a statically typed program, though obviously we don't gain anything by that at this point (this is essentially an argument that Bob Harper has advanced before) and we pay for it in additional verbosity. This is exactly the opposite of the argument I advance below; you're claiming that there is a "local" translation from dynamic to static. And on reflection, you might be right, but I'm not entirely sure. It hinges on exactly what counts as "local". Translating away exceptions, for example, requires inserting case-analyses which propagate an exception if one has been thrown literally everywhere in your program. It's "global" in the sense that it touches every node in the program; it's "local" in the sense that the transformations performed don't involve any global knowledge or analysis. I'm rereading Felleisen's paper now to see how precisely he defines things. ORIGINAL: > I had Matthias Felleisen's characterization of expressiveness [1] in mind (incidentally, he's also one of Racket's authors). Great! I believe, by that definition, your assertion that "good" typesystems don't reduce expressiveness is false, at least for common type systems such as Haskell's, Java's, ML's, etc. There are plenty of programs which require non-local rewriting ("a global reorganization of the entire program") in order to appease the typechecker. A simple example can be adapted from the expression problem: suppose I'm writing an compiler for a simple language, so I have an AST datatype and some functions on it. Then I add a pass to my compiler that removes a certain feature from the language I'm implementing - a certain branch of the AST. Now I wish to simplify my backend by removing the case which handles this branch of the AST. I cannot do that without either dynamically failing in that case (which a type system might even prevent me from doing) or introducing an entirely new AST type which does not have that branch - but this could require duplicating or else considerably generalizing existing code that operates on the AST (a global reorganization). Of course, a sufficiently powerful typesystem might let me do that, but for any typesystem you name, I suspect a similar example can be procured. To get more precise, the "feature" we're considering adding is "ignoring the typechecker and running anyway". For example, GHC's -fdefer-type-errors. The question is "can any legitimate program written with -fdefer-type-errors be locally translated into an equivalent program that typechecks without -fdefer-type-errors". Without reaching for an explicit escape hatch like unsafeCoerce (which is analogous to "unsafe" blocks in Rust), I think the answer is no.