← Back to context

Comment by sakras

17 days ago

I started reading it around a year ago with the personal goal of doing every exercise. I'm not done yet (almost done with chapter 4). It has definitely taken a lot of my time, but it really has changed my thinking about a lot of language constructs I find in other languages.

For instance, the part about tagging objects with their type reshaped my thinking about static type systems in general. Static typing, for example in C, is essentially just moving the location of the type tag from existing at runtime within the struct, to the "analysis" phase of the compiler. It's a pretty simple idea, but it made the "degree of dynamism" tradeoff click in my head. All the information _has_ to exist somewhere, and the only question is where. And Scheme is just at an extreme where the _everything_ is stored dynamically.

> Static typing, for example in C, is essentially just moving the location of the type tag from existing at runtime within the struct, to the "analysis" phase of the compiler.

See also "shift left"

> All the information _has_ to exist somewhere, and the only question is where.

Not entirely sure what you mean about this, but not all type information has to exist somewhere. Sometimes things get encoded into a type that might never be represented otherwise -- like whether an integer is a width or a height, or whether a string is a name or an address.

  • whether something (a variable) is a width or a height is encoded somewhere in the code because if the variable is a width it makes its usage different than if it is a height.

    Then the code is written to make sure you never make the mistake of sending a width to a height, because that would be silly.

    The type (width or height) is represented as logic in the code.

    theoretically - if you have a type system that allows you to know something is a width or height you can often reduce logic to keep track of these things but my experience is that level of granularity in your typing reduces dynamism too much. I would rather keep track of that in code (as it probably will have to deal with it anyway) than make my type system deal with it.

    • There's some cool mechanisms in type-forward languages that lead to minimal dynamism loss along with safety: For instance, you can do scientific programming that carries units along. So you really can divide a length by a time with no problems: Your results just happens to be a velocity, and will come out in meters per second (or whichever your favorite units are). Adding meters to kilometers? Congrats, you might you have to say which physical representation you want in float-land. But then you are saved from adding seconds to milliliters, because then the types will tell you it's complete nonsense.

      But note that to make all of that work well you really need your language to provide a lot of syntactic sugar features which are missing in most older languages, so all the operations read seamlessly, instead of being full of ceremony. One could do all of this in, say, old Java 1.4 or something, but the amount of type annotation, along with the massive mounts of boilerplate to get all of the operationg working right, and checked at compile time would make it all Not-Worth-It(tm) But with enough language features, and a smart enough compiler, `val acc = mySpeed / someSeconds` will work, straight up, with the safety included.

      14 replies →

  • At the extreme end, and with no claims of scalability, you have shell: everything is a string.

    I'm skeptical that sophisticated type systems need to exist.

    Perhaps I should dust off my copy and start reading SICP