
Types Are The Truth - mrbbk
http://michaelrbernste.in/2014/06/10/types-are-the-truth.html
======
ufo
> Types are there whether you want them to be or not

But this only applies to typed programs, right? For every type system you come
up with there will be some valid and normalizing term that cannot be typed
under that type system.

There is also a minor thing that came to my mind. Types are most often
associated with intuitionist logics, which are about constructive provability,
as opposed to classical logics, which are about "truthyness" so the title of
the post is a bit misleading :)

~~~
dllthomas
_" But this only applies to typed programs, right? For every type system you
come up with there will be some valid and normalizing term that cannot be
typed under that type system."_

In theory, yes - I came here to make that same objection.

In practice, there arises the question of whether such terms are actually ever
encountered for a given type system when solving realistic problems.

~~~
ufo
My impression is that these type system limitations start showing up once you
need to mix and match different type systems. For most terms there is a
reasonable type system that will type it but when you scale up to your whole
program/system then any single type system might not be able to cover all the
terms that you want to write. Some type system features such as parametric
polymorphism, subtyping, intersection types, linear types, etc are hard to mix
in practice, especially when you worry about decidability and type inference.

I think this is one of the reasons why dynamic languages are popular. It kind
of lets you mix different programming styles in a single program, at the cost
of laxer type checking (something I hope will get better in the future).

~~~
mkesper
The type checking isn't lax at all, at least not in Python.

~~~
ufo
The problem in Python is that type errors end up being reported later than
they would in a typed language. For example, if I have a function f that
expects an object and call it with f(None) its not going to immediately give
me an error. Its only going to give an error when I try to call a method on
that `None` and depending on how my program is structured the line with the
erroneous f(None) call might not even end up appearing in the final stack
trace.

------
dicroce
If you were building a sandcastle, a type system is like plastic forms in the
shape of castle components (walls, towers, etc)... They help you create a
better sandcastle...

This article was essentially saying: "You can remove the forms when you are
done and your sandcastle will still stand."

~~~
taeric
By that same analogy, building up a sand castle by only using those molds is a
ridiculously hard way to build some castles.

~~~
Lambdanaut
Fortunately some languages allow you to construct your own custom molds within
the language itself.

~~~
aikah
and in others,one isnt even allowed to use his hands...

~~~
kazagistar
Your hands are your mind... you think the types, check the types, and erase
them, but do all of it briefly, while playing with that part, and then move
away.

Compiled languages are like building a bunch of plaster mold, and then just
doing the castle in one step.

Optional typing, or using Any types and casting, is like using molds for a
nice foundation, and using your hands to add little bits on top.

------
dkarapetyan
Yes, type erasure is neat and a bit of thinking will make it obvious why
proving static properties does not require run time checks if the
transformations you perform on your program preserve those static properties.
I disagree with the author's claim though that types are everywhere. Types in
fact are extra structure that is hoisted onto programs. At the end of the day
the machine knows nothing about the types and will shuttle bytes back and
forth all day long. Furthermore, types restrict the universe of computation
and there are examples of programs that are not well-typed but don't go wrong
when evaluated.

~~~
mtdewcmu
I wonder if type erasure is profound, or if it only sounds profound after
being hypnotized by type theory.

~~~
dkarapetyan
Don't get me wrong I think type theory is really cool. In fact any kind of
theory that makes me a better programmer is cool. That's one of the reasons
TypeScript right now is one of my favorite languages. It is the perfect
balance between the prototyping power of dynamic languages and the awesome
static guarantees of statically typed languages. The best part is that the
type system does not get in the way when I'm prototyping and actually starts
to help out when I have the design fleshed out. I just wish more languages
supported that kind of type system because sometimes I really miss the static
checks when writing stuff in Ruby and Python.

~~~
mtdewcmu
What I think is cool is getting computers to do things that are interesting.
Types don't do anything. I find them hard to get excited about.

~~~
the_af
What do you mean "types don't do anything"? Are you seriously going to argue
that? I don't know many people in the programming or CS fields who believe
that. What _some_ people believe is that there are type systems too onerous
for the benefits they bring (which is, of course, debatable).

Following your line of thought, one could say that the textual representation
of your program "doesn't do anything" either, and neither does the syntax or
the IDE/editor you use. So none of it matters. But of course it wouldn't be
true -- all those things are useful tools that help you write software that
does cool things.

edit: thought of an even better example: tests. Tests "don't do anything".
They are not directly related to the cool stuff we want to do with our
computers. But who is going to argue that we shouldn't write any tests?

~~~
Dylan16807
I find it hard to get excited about normal testing.

~~~
the_af
You'll notice I only addressed the faulty assertion that "types don't do
anything". I've purposefully left the excitement part out, because that's
subjective. Some people _are_ excited about testing methodologies, some about
programming languages, some about their IDEs, etc.

What you can bet is that absolutely no-one is excited about mission-critical
software failing because of a bug that could have been caught by testing... or
type checking.

------
juliangamble
Some food for thought from Rich Hickey: "Statically typed programs and
programmers can certainly be as correct about the outside world as any other
program/programmers can, but the type system is irrelevant in that regard. The
connection between the outside world and the premises will always be outside
the scope of any type system."
[http://www.reddit.com/r/programming/comments/lirke/simple_ma...](http://www.reddit.com/r/programming/comments/lirke/simple_made_easy_by_rich_hickey_video/c2u1fgc)

~~~
gargantuan
Also what about protocols? Can type systems handle that. Things like:

    
    
      * open file
      * close file
      * read form file
    

That can be type checked up and down it will still be broken. Is that Rich
Hickey meant? Here we are dealing with a real world -- a file. And it has a
protocol to access it. So in a sense we want a protocol checker not a type
checker in that case...?

~~~
evincarofautumn
Separation logic can handle that—it’s a program logic that subsumes
typechecking and typestate, among other things. Your example, in pseudocode:

    
    
        // open : string -> file_mode -> (f : file) | f @ read * f @ close
        f = open "input.dat" Read
    
        // close : (f : file) | f @ read * f @ close -> ()
        close f
    
        // getline : (f : file) | f @ read -> string | f @ read
        s = getline f
    

“open” grants the “read” and “close” permissions to “f”; “close” revokes the
“read” permission that “getline” requires, causing “getline” not to typecheck.

------
webmaven
Hello, doppleganger! (I'm
[http://michaelbernstein.com](http://michaelbernstein.com))

~~~
mrbbk
Haha, hi there. As you can imagine, I've been to your homepage before.

~~~
webmaven
It's actually out of date, I'm currently in Kansas City.

Have you also been confused with
[http://hci.stanford.edu/msb/](http://hci.stanford.edu/msb/) or
[http://michaelbernstein.biz/](http://michaelbernstein.biz/) and even
[http://www.mabfan.com/](http://www.mabfan.com/) at SF conventions? ;-)

------
TheLoneWolfling
Isn't type erasure just a rather limited subclass of compiler optimization,
and as such comes "for free" without having to explicitly code for it? The
compiler frontend can leave in all type checks, having most of them can be
removed by the optimizer, the same way you can remove any sort of redundant
check (a default case in a switch statement over an enumeration, etc, etc)

That being said, general type erasure can have (massive) problems. Look at
Java.

~~~
lmm
I'd say Java's type erasure has on the whole been massively successful (and
it's allowed integration with e.g. Scala that a more reified type system might
make difficult). What do you see as wrong with it?

~~~
TheLoneWolfling
Too much wrestling with generics.

For example, having to use a varargs hack just to make a T[] (that's an array
of a a generic type).

Or not being able to call T.class

Or not being able to use instanceof T.

Or having a Object[] that crashes at runtime when you try to put an Object in
it.

Or not being able to overload a method based on if it's passed a List<Foo> or
List<Bar>. (Also applies to interfaces like Comparable, etc. There's no good
way of going "this is a Comparable<Foo> and a Comparable<Bar>, but not a
Comparable<Baz>.)

Or for that matter, trying to pass an int[] to something that expects an
Integer[], or vice versa.

Or for that matter, someone passing in a List into your parameter expecting
List<Foo> and crashing at runtime because it actually was a List<Bar>.

~~~
mcosta
It is ugly but you can make many of that things making all constructors
request a paramter Class<T> cls.

One thing you learn using java is not moving arrays around.

The comparator stuff is true.

Make the compiler to fail if you pass raw classes. A List is not a List<Foo>
and in some cases is not a List<?> (where List is a class of <T extends Foo>).

What I miss is some kind of self type, or "return this", I do not know how to
tell it. Imagine a imaginary class A:

    
    
        this aMethod() { 
            stuff(); 
            return this;
        )
    

This aMethod returns an A when invoked on an A instance or a B, B extends A,
if is a B instance. Handy for builders and fluid apis:

    
    
        B thisIsB = new B();
        thisIsB.aMethod().bMethod();

~~~
TheLoneWolfling
> One thing you learn using java is not moving arrays around.

And then all of a sudden you have an order of magnitude more memory usage
because you have a bunch of Characters as opposed to a bunch of chars.

~~~
mcosta
I use String(s)

~~~
TheLoneWolfling
Which doesn't help in the case I was discussing, namely when you want a
generic array. As in: generic. Not specific.

You end up having to use a collection, which ends up with an order of
magnitude more memory usage than an array in the case where you want to store
characters. (char is the worst case. But all primitives are pretty bad in that
regard.)

------
TheLoneWolfling
Personally, I wish that type systems allowed for arbitrary pure (as in "same
input -> same output") code to define what is and isn't a valid instance of a
type at compile time.

Being able to declare a function that is only valid for, say, power of two
inputs (say: a hashmap's initial size, when the hashmap is using a bitwise and
for wrapping) and having it actually enforced at compile time would be very
useful.

I mean, even range types are useful.

~~~
cousin_it
Any such system would need a theorem prover that's powerful and easy to use,
and such a prover doesn't exist. For example, to implement an algorithm that
involves arrays and indices, you'd need to supply machine-checkable proofs
that all indices involved in the algorithm are valid for their respective
arrays. That's really difficult, e.g. you can try to come up with some static
reasoning scheme that would work on this algorithm:

[http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pr...](http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm#Description_of_pseudocode_for_the_table-
building_algorithm)

The original paper on this topic is Hongwei Xi's "Eliminating array bound
checking through dependent types",
[http://www.cs.bu.edu/~hwxi/academic/papers/pldi98.pdf](http://www.cs.bu.edu/~hwxi/academic/papers/pldi98.pdf)
. You will note that he failed to remove all bound checks in the algorithm,
even though he started out with a good language (ML), added many manual
annotations, and used a sophisticated method of automatically proving
theorems. Also, his method and most subsequent methods will fail on any
problem that involves multiplication of integers to get array indices, e.g.
addressing a rectangular array as a[i*n+j], because integer arithmetic with
multiplication is undecidable and even simple instances make the computer cry.

This is a hard research problem. If you come up with a language design that is
guaranteed to be memory-safe, eliminates array bound checks in most realistic
use cases, and can be used by regular programmers, you will be hailed as a
hero. I'm not exaggerating at all.

~~~
TheLoneWolfling
Elaborating on my previous reply.

The way I wish things worked:

Programmers code with three types of constraints: soft cast, hard cast, and
unsafe cast. Soft cast is a runtime assertion that the constraint isn't
violated, hard cast is a "do not compile unless you can prove that this
constraint isn't violated" and unsafe cast is a "check at compile time to make
sure you cannot provide a counterexample, but do not include a runtime check".
Hard cast will be rarely used, for obvious reasons.

Compiler propagates known values / constraints forwards as much as possible,
and propagates constraints backwards as much as possible. Note that this
generally already happens as part of compiler optimizations. If at any point
any value violates a constraint, error out and provide a "constraint trace" of
what caused the violation.

After this step, the compiler looks at all unknown-at-compile time variables
and randomly (or rather, pseudo-randomly, maybe seeded with the md6 of the
file or something to be deterministic) generates and tests values (along with
some common values - note that this could be integrated with a test framework
built into the language!), ensuring that no constraints are violated. (There
are a bunch of optimizations here that I'm not going to mention.) If any
constraints are, error out with the variables required to violate the
constraint and the values assigned to them.

~~~
pinealservo
This is too much machinery to build into a general-purpose compiler today, but
there are static analysis tools (see Frama-C and the like) that will analyze
your program text (optionally with annotations like you've mentioned) and
figure out constraint violations.

------
eudox
The site is eerily similar to
[http://www.jonmsterling.com/index.html](http://www.jonmsterling.com/index.html)

You two could hit it off or something.

------
CmonDev
"Whether your programming language "embraces" types or has a modern type
system or not, these concepts are central to how programs execute, because
they are a fundamental part of how computation works." \- what are "modern"
type systems?

Was something new invented in the last 15-20 years that I had missed?

------
todd8
The discussion of types here is all over the place. This is because they are
handled so differently in various programming languages. Pascal included array
bounds in the type annotation for arrays and subscripting was enforced (at
runtime) to be withing bounds. In C (and C++) arrays are almost equivalent to
pointers (the full answer is complicated see [1]) and there is a real danger
of buffer overflows. Python doesn't include type annotations, but the run-time
enforces strong typing, lists carry their type information and will throw an
exception if subjected to a non-list operation. The type system in Haskell is
perhaps the best example of a really expressive and really effective system
for ensuring that the compiler can catch as many error as possible at compile
time.

Haskell programmers sometimes say that if a program compiles it almost always
works. This is interesting, but unfortunately, types and type annotations in
use today don't fully capture the formal specification needed to guarantee
program correctness.

Just writing down specification in preparation for a proof of correctness can
be difficult. The specifications have to be expressed in a formal system, for
example some form of mathematical logic like predicate calculus. Working with
anything less than a formal specification is like trying to solve a
mathematical set of equations without using math symbols. Natural language is
just inadequate to the task. See [2].

Getting the specifications right isn't easy. I remember my first attempt at
proving the correctness of a sorting program. I understood that I needed to
insure that A[i] <= A[i+1] for all elements in the array, but I forgot that
the final result had to include all and only the original elements (i.e. that
it had to be a strict permutation of the input elements). I don't believe that
type systems are currently useful for these kind of specifications and
verifications of program correctness.

Real programs interact with the outside world (e.g. GUI's are hard to describe
mathematically). Real programs often have distributed or concurrent execution
for which new logics (like Temporal Logic) may have to be employed. Proving
that a program will terminate or make progress or not deadlock or not livelock
or will meet some real-time constraints are all exceptionally hard.

Many years ago (in the 1980's) I worked on this research area at University. I
had the hope that eventually, programming would be more like an interactive
exploration with a sophisticated program prover. I thought that the programmer
and software-prover would work together to produce a correct program. Today,
we are still programming in pretty much the same old ways as back then. The
languages are much better, but it's a harder problem than I thought it was
going to be.

[1] [http://stackoverflow.com/questions/3959705/arrays-are-
pointe...](http://stackoverflow.com/questions/3959705/arrays-are-pointers) [2]
[https://www.cs.utexas.edu/users/EWD/transcriptions/EWD06xx/E...](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD06xx/EWD667.html)

