7 ms·
The following is based on conjecture, as I'm not old or well-read enough to be sure of this, but it seems to me that the original purpose of type systems got mu
by benaiah 12y ago
The following is based on conjecture, as I'm not old or well-read enough to be sure of this, but it seems to me that the original purpose of type systems got muddled up by the tremendous popularity, mostly in Windows systems, of "hungarian" variable naming style. When your "type system" consists of adding three letters to the beginning of a variable name, you don't have a way to make descriptive types. Java, in most of the ways I've seen it used, is essentially hungarian notation enforced as a language feature.
I realize that variable naming and type systems are two different things, but it seems many programmers never realized the point of type systems (expressing the type for the programmers sake) because they only ever saw it used to distinguish abstract primitives. For a long time, I had trouble understanding that hungarian notation wasn't a type system, because they seemed to do precisely the same thing - that's how limited my understanding of types was. The kind of style seen in this article was alien to me for a long time, but it was enlightening to realize that the type system is there to help me out, not just make me type a bunch of unnecessary crap.
tl;dr: I really need to learn Haskell.
- jarrett 12y agoI think what you're getting at is the distinction between this: float x; and this: kilogram x; The former, as you say, only tells you about the underlying representation. It says "this is a float, so the computer should store it in such and such a way." That's fine, but it doesn't tell us enough. The latter example is far more useful. In my imaginary language, the declaration implicitly tells the computer to store the value as a float, because the kilogram type has been defined as such elsewhere. But that's not all it does! It tells us and the compiler that this float represents a real-world quantity measured in kilograms. It prevents us from mistakenly passing kilograms where a pounds were expected, or seconds where kilograms were expected. On the Haskell front, you might be interested in the Dimensional library, which does just that. It also works elegantly with multiplying and dividing units. E.g. if you have a miles value and an hours value, you can divide to get a miles per hour value.
- benaiah 12y agoYeah - this is exactly the kind of distinction I was trying to make. Reminds me of Abelson's famous quote, "Programs must be written for people to read, and only incidentally for machines to execute." That's an idea that's largely forgotten when it comes to type systems. Thanks for the pointer - I'll be sure to check out the library as I dive into Haskell over the next few months.
- robert_tweed 12y agoI've had a long standing interest in this kind of semantic typing. Ironically, one of the things people hate about Java is that it has a deeply engrained culture of using semantic typing everywhere, except that it uses class "boxing" rather than just having something like the type system in Go (typedef on steroids) or something axiom based like Haskell. So that's why so much Java code ends up looking like this: //class FurryThing implements Thing {} //class RubberThing implements Thing {} FuzzyThing furbie = new FuzzyThing(); RubberThing bouncyball = new RubberThing(); DoStuffToThing( Thing(furbie) ); DoStuffToThing( Thing(bouncyball) ); In fact, it's not uncommon to see things like class Furbie extends FuzzyThing, so you end up with very specific types that don't necessary behave differently, but do add some supposed semantic precision to the code. Unfortunately, you don't get much benefit from it in Java, so it just ends up being a hassle, resulting in lots of programmer-forced unsafe casting and just as many runtime errors as you'd have got with simpler types. This is not a fault of the language, just poor implementation which rightly or wrongly appears to be idiomatic in Java. The type system in, for example, Go is much simpler, and allows you to create semantic types that actually are the underlying type instead of creating a class wrapper (called boxing in Java, e.g., boxed int is class Integer, which is an object containing only an int), which means you can define a type like 'kilogram' that's really just a float, but the type system won't silently cast a 'kilogram' value to a float or vice-versa because it knows they are different things. I haven't got into Haskell in much detail yet but it just basically takes that a few steps further and allows you to define the characteristics of each custom type based on axioms, that describe things like whether it's commutative or associative, the range of allowed values, etc. This kind of axiomatic semantic typing is clearly a lot more powerful, but as yet it hasn't really found its way into mainstream languages yet. Go takes the view that it's too complicated for what is intended to be a low-level language. Rust looks interesting because it aims to be equally low level, but provide some of the same rich typing you'd find in Haskell. Unfortunately it's far from stable, but it'll be interesting to see where it ends up.
- steveklabnik 12y agoYou can even take this a step further: using `float` instead of `kilogram` is leaking an implementation detail. It's (probably) too low-level of information to actually be useful. Luckily, more and more programming languages are making creating these simple types easier. Haskell: newtype Kilogram = Integer Rust: struct Kilogram(int); In fact, this blog post, while a bit outdated, shows an application of this idea, to solve string encoding issues for HTML templating: http://bluishcoder.co.nz/2013/08/15/phantom_types_in_rust.html http://bluishcoder.co.nz/2013/08/15/phantom_types_in_rust.ht...
- tutuca 12y agoor good ol' typedef, no?
- deleted 12y ago[deleted]
- steveklabnik 12y agoSure, but typedefs aren't as strong a guarantee. They're more a textual find/replace than an actual feature of the type system.
- masklinn 12y agoWith a straight typedef, you can add 3 feet to 5 meters and get 8. Probably not desirable. So you've got to typedef single-field structs instead.
- dllthomas 12y ago"So you've got to typedef single-field structs instead." Which, thankfully, don't add any runtime cost (as should be expected). As an aside, you don't actually need to typedef them at that point, but it saves you having to write "struct ..." everywhere so it's typically worthwhile.
- sparkie 12y agonitpick - the haskell version should be newtype Kilogram = Kilogram integer The issue with doing this of course, is that it's mostly useless to computation. You lose the ability to use mathematical operators because you're no longer an instance of Num, and even if you create the Num instance, or use -XGeneralizedNewtypeDeriving, you can't multiply a Kilogram by a Meter/Second for example, since the arguments to (*) must be of the same type. One would need to use a generic "Measure" type instead, where the unit is some metadata attached to it, and the Num instance implements the typechecking on units.
- xenomachina 12y agoDoes the Dimensional library work for weird units like Fahrenheit and Celsius (ie: where zero is in the wrong place)?
- jarrett 12y agoCelsius and Kelvin are supported. I'm not certain, but it would appear Fahrenheit support is not built-in. There's a module called NonSI: https://hackage.haskell.org/package/dimensional-0.13/docs/Numeric-Units-Dimensional-NonSI.html https://hackage.haskell.org/package/dimensional-0.13/docs/Nu... I would have expected Fahrenheit to be in that module if it were supported. But I presume you could add Fahrenheit yourself, if you were willing to learn a bit about the library's internals.
- nagisa 12y agoFahrenheit is not easily addable given current realisation of functionality and, if I recall correctly, somewhere in source code there’s a comment in which author explicitly states he doesn’t support units which don’t linearly correspond to SI-based unit.
- jimktrains2 12y agoThe function to convert Fahrenheit to Celsius is a linear function of C and K?
- cobbal 12y agoLinear in this case means y = m * x. The relationship between Fahrenheit and Celsius is affine (y = m * x + b) For gory details: https://en.wikipedia.org/wiki/Affine_transformation https://en.wikipedia.org/wiki/Affine_transformation
- judk 12y agoCelsius is an affine transformation of Kelvin
- skybrian 12y agoAs an alternative, check out how Go does this. It's a nice compromise that lets you declare kilogram to be a float, and when used alone it treats it as a separate type, but it doesn't get in the way when you use it in an expression.
- Jtsummers 12y agoThat seems useful, but dangerous. If you specify (for some reason) both a centimeter and inches type, would you be able to mix them in expressions without warnings? -------- Per [1] it seems that since both would be named types you wouldn't be able to assign one to the other without explicit conversions. If my reading is correct, then that addresses my concern. It's things like storing a `float` to a `inch` type that can happen without explicit conversion as long as `inch` has float as its base type. [1] http://golang.org/ref/spec#Properties_of_types_and_values http://golang.org/ref/spec#Properties_of_types_and_values
- skybrian 12y agoThey are separate types, not a type alias, but some conversions are automatic. The rule for assignability also affects comparisons and function calls (including expressions containing them), but apparently not all expressions like I thought?
- Dewie 12y ago> type Kilogram = Double type synonym/alias.
- jarrett 12y agoIn Haskell? I wouldn't do that. That improves readability, but it doesn't allow the compiler to enforce correct unit usage. If a Kilogram is just a synonym for a Double, you can pass it in where other meanings of Double are expected. E.g. you can pass Kilograms into a function that takes Pounds. Which you don't want to do.
- 12y ago
- pacaro 12y agoF# has this as units of measure [1] [1] http://msdn.microsoft.com/en-us/library/dd233243.aspx http://msdn.microsoft.com/en-us/library/dd233243.aspx
- kirab 12y agoIn C++14 this got easier. Declaration: struct Kilogram {float v;}; Instantiation: Kilogram x = {1}; or Kilogram x{1};
- coolsunglasses 12y agoI've been teaching a bunch of Haskell lately, this is my guide for learning it: https://gist.github.com/bitemyapp/8739525 https://gist.github.com/bitemyapp/8739525 And as the other commenter mentioned, Dimensional is indeed a cool library :)