49

In JavaScript: The Good Parts by Douglas Crockford, he mentions in his inheritance chapter,

The other benefit of classical inheritance is that it includes the specification of a system of types. This mostly frees the programmer from having to write explicit casting operations, which is a very good thing because when casting, the safety benefits of a type system are lost.

So first of all, what actually is safety? protection against data corruption, or hackers, or system malfunctions, etc.?

What are the safety benefits of a type system? What makes a type system different that allows it to provide these safety benefits?

10
  • I'm not certain that type systems provide any benefit for non-compiled language, but as a long term user of compiled languages, I find that compiled languages with careful type checking are effective in preventing many kinds of ambiguous, undefined or incomplete code from getting past the "compile" stage. I guess you could say that type hints and a Lint system are valuable for Web Scripting (JavaScript) and if so, I'm sure we'll see enough of them. Dart anyone? Dynamic languages like Python seem no worse for the lack of a static type system.
    – Warren P
    Oct 25, 2013 at 0:40
  • 1
    Today we understand that typing should be behavioral and not structural. Sadly, most modern programming languages have no way to assert the behavior of a type (see this question for a nice read). This makes the type system pretty useless in most cases especially since simple type errors that answers mention here can be caught by a clever linter that checks for common issues. Oct 25, 2013 at 1:56
  • 4
    @BenjaminGruenbaum What your describing already exists in languages like OCaml statically. It's called structural typing, it's actually quite old, nominal typing is newer. Oct 25, 2013 at 2:41
  • 2
    @BenjaminGruenbaum: ...What!? It is obviously not undecidable in statically-typed languages, or else writing a compiler for those languages would be impossible. Oct 25, 2013 at 17:27
  • 6
    @BenjaminGruenbaum: Your comments are valuable, and that paper is interesting, but it does not bear out your claim that "it's usually undecidable in static languages like Java too", since it demonstrates that it is decidable in C#, and leaves open the question of whether it's undecidable in Java. (And anyway, IME, when a compiler for a statically-typed language can't decide that something is well-typed, it rejects it (or fails to compile it), so undecidability is an annoyance rather than a hole in the type-safety.)
    – ruakh
    Oct 26, 2013 at 22:55

8 Answers 8

85

Type systems prevent errors

Type systems eliminates illegal programs. Consider the following Python code.

 a = 'foo'
 b = True
 c = a / b

In Python, this program fails; it throws an exception. In a language like Java, C#, Haskell, whatever, this isn't even a legal program. You entirely avoid these errors because they simply aren't possible in the set of input programs.

Similarly, a better type system rules out more errors. If we jump up to super advanced type systems we can say things like this:

 Definition divide x (y : {x : integer | x /= 0}) = x / y

Now the type system guarantees that there aren't any divide-by-0 errors.

What sort of errors

Here's a brief list of what errors type systems can prevent

  1. Out-of-range errors
  2. SQL injection
  3. Generalizing 2, many safety issues (what taint checking is for in Perl)
  4. Out-of-sequence errors (forgetting to call init)
  5. Forcing a subset of values to be used (for example, only integers greater than 0)
  6. Nefarious kittens (Yes, it was a joke)
  7. Loss-of-precision errors
  8. Software transactional memory (STM) errors (this needs purity, which also requires types)
  9. Generalizing 8, controlling side effects
  10. Invariants over data structures (is a binary tree balanced?)
  11. Forgetting an exception or throwing the wrong one

And remember, this is also at compile time. No need to write tests with 100% code coverage to simply check for type errors, the compiler just does it for you :)

Case study: Typed lambda calculus

Alright, let's examine the simplest of all type systems, simply typed lambda calculus.

Basically there are two types,

Type = Unit | Type -> Type

And all terms are either variables, lambdas, or application. Based on this, we can prove that any well typed program terminates. There is never a situation where the program will get stuck or loop forever. This isn't provable in normal lambda calculus because well, it isn't true.

Think about this, we can use type systems to guarentee that our program doesn't loop forever, rather cool right?

Detour into dynamic types

Dynamic type systems can offer identical guarantees as static type systems, but at runtime rather than compile time. Actually, since it's runtime, you can actually offer more information. You lose some guarantees however, particularly about static properties like termination.

So dynamic types don't rule out certain programs, but rather route malformed programs to well-defined actions, like throwing exceptions.

TLDR

So the long and the short of it, is that type systems rule out certain programs. Many of the programs are broken in some way, therefore, with type systems we avoid these broken programs.

23
  • 26
    +1 for compiling as the equivalent to writing a lot of tests. Oct 24, 2013 at 19:10
  • 3
    @DanNeely It's merely to illustrate that in a dynamic language, you need to exercise all parts of the code to catch the errors which a type system checks for free. And in a dependently typed language, you actually can entirely replace tests with types. Many times you need to prove additional theorems of correctness though Oct 24, 2013 at 19:14
  • 5
    @DonalFellows The Ackermann function isn't primitive recursive, although it is a total computable function.
    – Taymon
    Oct 25, 2013 at 0:59
  • 4
    @sacundim Exactly, languages like agda allow for optional totality checking and in the rare cases where you do want arbitrary recursion you can ask nicely, it's quite a slick system. Oct 25, 2013 at 2:37
  • 4
    Another great example for what type systems can verify: units of measure. This even includes automatically converting between compatible units like feet and meters! Oct 25, 2013 at 20:33
27

Reality itself is typed. You can't add lengths to weights. And while you can add feets to meters (both are units of lengths), you should scale at least one of the two. Failing to do so can crash your Mars mission, quite literally.

In a typesafe system, adding two lengths expressed in different units would have been either an error or would have caused an automatic cast.

1
  • 1
    Great answer and even better than the selected answer. You could also add that with type safety, the compiler checks most of the type errors for you. Without type safety, you'd have to write test code to do that. Thus, the rocket crashes and explodes on your launch site itself and your site becomes like Mars. Therefore, mission (still) accomplished.
    – MasterJoe
    Apr 18, 2020 at 19:12
15

A type system helps you avoid simple coding errors, or rather allows the compiler catch those errors for you.

For example, in JavaScript and Python, the following problem will often only be caught at runtime - and depending on testing quality/rarity of the condition may actually make it to production:

if (someRareCondition)
     a = 1
else
     a = {1, 2, 3}

// 10 lines below
k = a.length

While a strongly-typed language will force you to explicitly state that a is an array and will not let you assign an integer. In this way, there isn't any chance a won't have length - even in the rarest cases.

7
  • 5
    And a clever linter in an IDE like WebStorm JavaScript can say "Possible undefined reference to a.length for number a". This is not given to us by having an explicit type system. Oct 25, 2013 at 1:58
  • 4
    1. Statically not strongly 2. @BenjaminGruenbaum Yes, but this is done by chasing a graph of assignments in the background, think of it like a mini interpreter trying to figure out where things are going. Much harder than when types give you it for free Oct 25, 2013 at 2:33
  • 6
    @BenjaminGruenbaum: Don't confuse implicit/explicit with strong/weak. Haskell, for example, has an incredibly strong type system that puts most other languages to shame, but due to certain language design decisions made it's also capable of almost entirely universal type inference, essentially making it a strongly implicit typed language with support for explicit typing (That you should use, because the type inferencer can only deduce from what you wrote, not what you meant!)
    – Phoshi
    Oct 25, 2013 at 8:14
  • 7
    “strongly-typed language will force you to explicitly state that a is an array” That’s wrong. Python is strongly typed and doesn’t require that. Even statically-and-strongly typed languages don’t require that if they support type inference (and most mainstream languages nowadays do, at least partly). Oct 25, 2013 at 10:00
  • 1
    @BenjaminGruenbaum: Ah, fair enough. Even so, there will be cases where no JS static analyser can perform the same kinds of typecheck a strongly typed language would provide, solving that in the general case requires solving the halting problem. Haskell had to make a fair few design decisions in order to achieve near-100% type inference, and C#/Scala can't infer everything. Of course, in those cases, it doesn't matter because you can just explicitly specify types--in Javascript, it means even the best static analyser can no longer check your code.
    – Phoshi
    Oct 25, 2013 at 20:53
5

The earlier in the software development cycle you can catch an error, the less expensive it is to fix. Consider an error that causes your biggest client, or all your clients to lose data. Such an error could be the end of your company if it is only caught after real customers have lost data! It is clearly less expensive to find and fix this bug before moving it to production.

Even for less costly errors, more time and energy is spent if testers are involved than if programmers can find and fix it. It's cheaper if it does not get checked into source control where other programmers can build software that relies on it. Type safety prevents certain classes of errors from even compiling, thus eliminating almost the entire potential cost of those errors.

But that isn't the whole story. As anyone who programs in a dynamic language will tell you, some times it's nice if your program just compiles so you can try out part of it without getting every little detail to work out. There is a trade-off between safety and convenience. Unit tests can mitigate some of the risk of using a dynamic language, but writing and maintaining good unit tests has its own cost which may be higher than that of using a type-safe language.

If you are experimenting, if your code will only be used once (such as a one-time report), of if you are in a situation where you wouldn't bother to write a unit test anyway, then a dynamic language is probably perfect for you. If you have a large application and want to change one part without breaking the rest of it, then type safety is a life saver. The types of errors type safety catches are exactly the kind of errors that humans tend to overlook or get wrong when refactoring.

3
  • This sells dynamic typing short, failing to mention its primary benefits (the ones mentioned are handy by relatively unimportant). It also seems to imply something strange about unit tests -- yes, they're hard to do and have a cost, and that applies to statically-typed languages as well. What is this trying to say? It also fails to mention the limitations (by design) of current type systems, both in what they can express and in what errors they can catch.
    – user39685
    Dec 12, 2013 at 10:46
  • @MattFenwick what do you feel are the primary benefits of dynamic typing? Dec 12, 2013 at 16:00
  • Typical static type systems reject many well-typed programs by design. (an alternative) (BTW, my criticism was only directed at the 3rd and 4th paragraphs.)
    – user39685
    Dec 19, 2013 at 16:40
4

Introduction

Type safety can be achieved with either statically-typed (compiled, static type checking) and/or runtime (evaluated, dynamic type checking) languages. According to Wikipedia a '... strong type system is described as one in which there is no possibility of an unchecked runtime type error (ed Luca Cardelli). In other writing, the absence of unchecked run-time errors is referred to as safety or type safety ...'

Safety - Static Type Checking

Classically, type safety has been synonymous with static typing, in languages such as C, C++ and Haskell, that are designed to detect type mis-matches when they are compiled. This has the benefit of avoiding potentially undefined or error-prone conditions when the programme is executed. This can be invaluable where there is a risk that pointer types may be mis-matched, for example, a situation that could lead to catastrophic consequences if not detected. In this sense static typing is considered synonymous with memory safety.

Static typing is not completely safe but enhances safety, however. Even statically-typed systems can have catastrophic consequences. Many experts consider that statically-typed can be used to write more robust and less error-prone (mission critical) systems.

Statically-typed languages can help to reduce the risk of loss of data or loss of accuracy in numerical work, that can occur due to mis-matching or truncating double to float or mis-matching integral and float types.

There is an advantage in using statically-typed languages for efficiency and speed of execution. The runtime benefits from not having to determine the types during execution.

Safety - Runtime Type Checking

Erlang, for example, is a type declarative, dynamically type checked language that runs on a virtual machine. Erlang code can be byte compiled. Erlang is considered perhaps the most important mission-critical, fault tolerant language available, and it is reported that Erlang has a reliability of nine 9's (99.9999999% or not more than 31.5 msecs per year).

Certain languages, such as Common Lisp, are not statically-typed but types can be declared if desired which can help improve speed and efficiency. It is also to be noted that many of the more widely used interpreted languages, such as Python, are, underneath the evaluation loop, written in statically-typed languages such as C or C++. Both Commom Lisp and Python are considered type safe by the definition above.

10
  • 2
    I object to "strongly typed". You mean statically typed. Strongly type carries pretty much no meaning, it's used to basically say "I like this type system" Oct 24, 2013 at 19:21
  • @ jozefg Good point. I will amend the post.
    – AsymLabs
    Oct 24, 2013 at 19:24
  • 3
    It is also not useful to say interpreted language... about a language implementation yes, but not the language itself. Any language can be interpreted or compiled. And even after the edit you are using the terms strong and weak typing.
    – Esailija
    Oct 24, 2013 at 20:24
  • 3
    @jozefg: I always thought that strongly typed meant that each value has a fixed type (e.g. integer, string, etc), whereas weakly typed means that a value can be coerced to a value of another type, if it is considered convenient to do so. E.g., in Python (strongly typed), 1 + "1" throws an exception, whereas in PHP (weakly typed) 1 + "1" produces 2 (string "1" is automatically converted to integer 1).
    – Giorgio
    Oct 24, 2013 at 21:06
  • 1
    @Giorgio with such definition for example Java is not strongly typed. But in many cases it is claimed to be. There is just no meaning to these words. Strong/Weak typed have much more accurate definition as "I like/don't this language" like jozefg says.
    – Esailija
    Oct 24, 2013 at 22:04
1

the safety benefits of a type system are lost.

So first of all, what actually is safety? protection against data corruption, or hackers, or system malfunctions, etc.?

What are the safety benefits of a type system? What makes a type system different that allows it to provide these safety benefits?

I feel like type systems have such a negative view. A type system is more about making a guarantee than about proving the absence of errors. The latter is a consequence of the type system. A type system for a programming language is a way to produce, at compile time, a proof that a program meets some kind of specification.

The kind of specification that one can encode as a type depends on the language, or more directly, on the strength of the language's type system.

The most basic kind of specification is a guarantee about the input/output behaviour of functions and of the validity of the inside of a function body. Consider a function header

f : (Int,Int) -> String

A good type system will make sure that f is only applied to objects that will produce a pair of Int when evaluate, and guarantees that f will always produce a string.

Some statements in a language, like if-then blocks, don't have an input/output behaviour; here the type system guarantees that each declaration or statement in the block is valid; that is applies operations to objects of the correct kind. These guarantees are composable.

Also, this does give a sort of memory safety condition. The quote you are dealing with is about casting. In some cases, casting is fine, like casting a 32-bit Int to a 64-bit Int. However, generally, it does crash the type system.

Consider

Foo x = new Foo(3,4,5,6);
f((Int)x,(Int)x);

Because of casting, x is turned into an Int, so technically the above does type check; however, it really does defeat the purpose of typechecking.

One thing that could make a different and better type system is to dissallow casts (A)x where x before the case is type B, unless B is a subtype (or subobject) of A. The ideas of subtyping theory have been used in security to remove the possibility of integer overflow/underflow attacks.

Summary

A type system is a way to prove a program meets some kind of specification. The benefits a type system can provide depend on the strength of the type system used.

1

One advantage not yet mentioned for a type system centers around the fact that many programs are read more than they are written, and in many cases a type system may allow a lot of information to be specified in a manner which is concise and can be easily digested by someone reading the code. While parameter types don't take the place of descriptive comments, most people will find it faster to read: "int Distance;" or Distance As Int32 than to read "Distance must be a whole number +/- 2147483647"; passing fractions may yield inconsistent results." Further, parameter types can help reduce the gap between what a particular implementation of an API happens to do, versus what callers are entitled to rely upon. For example, if a particular Javascript implementation of an API uses its parameters in a way which would coerce any strings to numeric form, it may be unclear whether callers are allowed to rely upon such behavior, or if other implementations of the API might malfunction if given strings. Having a method whose parameter is specified as Double would make it clear that any string values must be coerced by the caller before being passed; having a method with an overload that accepts Double and another that accepts String would make it somewhat clearer that callers holding strings would be allowed to pass them as such.

0

So first of all, what actually is safety? Protection against data corruption, or hackers, or system malfunctions, etc.?

All of the other answers and more. In general, "type safety" simply means that none of the programs a compiler successfully compiles will contain type errors.

Now, what is a type error? In principle, you can specify any undesirable property as a type error, and some type systems will be able to statically ensure no program has such an error.

By "property" above, I mean some type of logical proposition that applies to your program, for instance, "all indices are within array bounds". Other types of properties include, "all deferenced pointers are valid", "this program doesn't perform any I/O", or "this program performs I/O only to /dev/null", etc. Just about any sort of property can be specified and type checked in this way, depending on the expressiveness of your type system.

Dependent type systems are among the most general of type systems, via which you can enforce pretty much any property you like. It's not necessarily easy to do so though, as sophisticated properties are subject to incompleteness courtesy of Gödel.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.