Live data from Hacker News

Make the Type System Do the Work

nathan.ca

81–90 of 141 posts

Re: Make the Type System Do the Work

#81

Earlier quoted context omitted.

"Dog is-a Mammal" (or "Circle is-a Ellipse") is sound, what is unsound is supporting mutations that alter identity. If you change the minor axis of an ellipse, it isn't the same ellipse . The idea of a mutating squash method of the type derived is therefore unsound in the context of the domain being modeled, as it would alter identity. > (This example uses shapes, because the idea of mutating mammals gets a little st…

"what is unsound is supporting mutations that alter identity" EDIT: included code for clarity I can make essentially the same example with concrete entities that exist in time and space, as well: void f1() { GasolineVehicle vehicle(myEngine); f2(vehicle); cout After replacing the engine with a motor, MPG no longer makes sense. At the time you're writing the Vehicle class, it seems perfectly reasonable to define a set…

I think it's a fundamentally bad idea to think of class hierarchies as ontological schemata of real world things.

It's like ancient philosophers trying to define the human being. Are we FeatherlessBipeds or RationalAnimals? Who really cares?

The Circle/Ellipse quabble is academic because it never mentions the actual domain – for what purpose are we trying to model these entities in the first place?

Even in pure geometry, there are many different ways to classify and treat these structures. If you have a generic shape representation, you could just have Circle and Ellipse as different builder functions. If you're mostly just concerned with drawing, then a simple "isCircular" method on the Ellipse object might be what you need. And so on...

Re: Make the Type System Do the Work

#82
post #26

Did anyone else recoil in horror at how a simple function is turned into a type hierarchy? This is everything that's wrong with Java to me. Classes are all fine and well, but don't take it to extremes. Maybe the examples were just too trivial, but it really is a far greater evil than just using well named variables and a simple conversion function.

I think the examples are just too trivial (or in this case, aren't extended to a greater library). Where they really come in handy is if you're using the types all over the place. If I were to create a Degrees class (with Celsius and Fahrenheit) just for representing it in a UI, I'd call it crazy. But if I'm doing conversions all over the place or creating a library for others to use, I would think the plumbing is wo…

If you are "doing conversions all over the place", you are looking at a fundamentally flawed code design that costs you performance. Ensuring consistency of coversions here is like painting a house whose roof's on fire.

C/F example is synthetic, but it doesn't make it any good - if you are operating with data that can be expressed in different units, then pick a single unit, stick with it throught the code and convert only when displaying (or passing to external party code).

Re: Make the Type System Do the Work

#83

This article uses inheritance in C++ to ensure the types line-up, but I have really taken a shine to algebraic data types for my firm's data feed handlers. Here is an example in C++11 that implements a market-data spec line-for-line. #pragma pack(push,1) struct Quote { char symbol[16]; uint16_t bid; uint32_t bidsize; uint16_t ask uint32_t asksize; }; static_assert(sizeof(Quote) == 28, "Quote size wrong"); struct Trad…

That's how I write LuaJIT code for networking and device drivers too. Very neat.

Re: Make the Type System Do the Work

#84

This article uses inheritance in C++ to ensure the types line-up, but I have really taken a shine to algebraic data types for my firm's data feed handlers. Here is an example in C++11 that implements a market-data spec line-for-line. #pragma pack(push,1) struct Quote { char symbol[16]; uint16_t bid; uint32_t bidsize; uint16_t ask uint32_t asksize; }; static_assert(sizeof(Quote) == 28, "Quote size wrong"); struct Trad…

What if your bytes are 16 bit on a given platform? ;)

Re: Make the Type System Do the Work

#85

This article uses inheritance in C++ to ensure the types line-up, but I have really taken a shine to algebraic data types for my firm's data feed handlers. Here is an example in C++11 that implements a market-data spec line-for-line. #pragma pack(push,1) struct Quote { char symbol[16]; uint16_t bid; uint32_t bidsize; uint16_t ask uint32_t asksize; }; static_assert(sizeof(Quote) == 28, "Quote size wrong"); struct Trad…

If I remember correctly there was even a special clause in the C standard definition of union types which allows you to write:

struct A { Data header; ... }

struct B { Data header; ... }

union AB { Data header; A a; B b }

and then access the header field in AB. If this is syntactically the first field in each of the constituent records the result is well-defined.

This is a great solution if you need to map to a specific wire format. If you do not have that constraint and have code which uses algebraic data types a lot you should probably switch to OCaml or Haskell. There are specific optimizations to work with complex matches and for representing algebraic types in a compact and efficient way. In C++ the compiler has to work with reinterpret_casts and cannot change the representation of your data types (much). Doing the optimizations by hand is possible, but chances are that a simple-minded OCaml prototype will already perform reasonably well.

Re: Make the Type System Do the Work

#86

This article uses inheritance in C++ to ensure the types line-up, but I have really taken a shine to algebraic data types for my firm's data feed handlers. Here is an example in C++11 that implements a market-data spec line-for-line. #pragma pack(push,1) struct Quote { char symbol[16]; uint16_t bid; uint32_t bidsize; uint16_t ask uint32_t asksize; }; static_assert(sizeof(Quote) == 28, "Quote size wrong"); struct Trad…

Its clever but it doesnt scale to writing systems. Its just a hack.

Hmm? I disagree. I have built very large systems using exactly this technique.

As I mentioned in a comment above, lots of on the wire binary protocols start with a type identifier byte (or word). You then have a wonderful switch statement based off of that.

Heck I had an entire inter processor communication system running based on what was essentially this very idea. The switch actually had two layers, your first byte gave you a subsystem to jump to and the second byte was the command within that subsystem.

You sent data down the wire connecting the two processors together (a UART connection in this case), read straight into a buffer, and just pop right into a switch statement. The main disadvantage is everything is set at compile time, but for a lot of uses that is a-ok.

Re: Make the Type System Do the Work

#87
post #20

In some environments beware of creating new types and objects: http://developer.android.com/training/articles/perf-tips.htm...

Yes, I noticed that some time ago and ended up rewriting parts of the application (and also avoiding generics, instead opting for HPPC). What annoys me a bit is that Dalvik has dramatically different performance characteristics from normal Java which necessitates relearning all this stuff. Nice article though and I wish I had found it sooner.

Re: Make the Type System Do the Work

#88
post #62

Earlier quoted context omitted.

But this solution doesn't solve the original problem described in the article. With your API, it is easy for a programmer to use a temperature in Celcius as a temperature in Kelvins and the type system can't catch it.

Well, most code can just pass around Temperature objects without dealing with any particular units. There's no opportunity to mix up units there. When some code needs to actually get a number out, say for the purpose of logging, it's possible to screw up with either approach. With the author's classes, you could do void printTemperature(DegCelsius temperature) { log("Temperature in Kelvin: %f", temperature.getDegrees…

With the author's code you can define a print() function inside each temperature class, so that there is no mixup of units during printing. Or let the class supply its suffix instead of hardcoding it in an external print() function. It might not be always possible to follow that technique, but in general, it is better (according to the article, and I agree) to retain as much information at compile time as possible.

Re: Make the Type System Do the Work

#89
post #82

Earlier quoted context omitted.

I think the examples are just too trivial (or in this case, aren't extended to a greater library). Where they really come in handy is if you're using the types all over the place. If I were to create a Degrees class (with Celsius and Fahrenheit) just for representing it in a UI, I'd call it crazy. But if I'm doing conversions all over the place or creating a library for others to use, I would think the plumbing is wo…

If you are "doing conversions all over the place", you are looking at a fundamentally flawed code design that costs you performance. Ensuring consistency of coversions here is like painting a house whose roof's on fire. C/F example is synthetic, but it doesn't make it any good - if you are operating with data that can be expressed in different units, then pick a single unit, stick with it throught the code and conver…

Or perhaps you're integrating a large number of systems, each with its own conventions.

Re: Make the Type System Do the Work

#90
post #39
post #28

Earlier quoted context omitted.

I don't think there's going to be any performance difference at all compared to the basic case, which actually is another reason why this is so good. Ultimately, all these struct just hold a double, which means that in memory they're exactly a double and nothing more, there's no type tag or anything like that. And all the code is static so there is no vtable, just the double value. The compiler does the rest, not the…

It can make a difference due to weird ABIs. For example, in the ARM procedure call standard, 64-bit values can be returned in r0 and r1, but 64-bit structures can't (no matter whether they contain two 32-bit values or just one 64-bit value). However, I'd call that fairly niche - it's hard to imagine an application that would notice a significant performance difference.

This is very nice to know actually, and quite relevant to code I am working on right now!

Disappointing though.

> it's hard to imagine an application that would notice a significant performance difference.

One hopes any function call made often enough to be a perf hit from this would also be designed to be easily inlined!

Post reply on HN