DEV Community

stereobooster
stereobooster

Posted on

Pragmatic types: what are types?

The idea of this post is to give you a framework to reason about types (in programming), I'm not gonna try to give an exhaustive and full mathematically correct definition of types.

Also, some mathematicians argue there is no single definition of the types and this is good.

Definition

Type is a collection of items, often having some common properties, structure and operations permitted for this type.

I intentionally use the word "collection" instead of "set", because the set has the exact meaning in mathematics.

For example

Cars: 🚙, 🚌, 🚜.
Fruits: 🍋, 🍐, 🍓.
Enter fullscreen mode Exit fullscreen mode

Also, we can define operations for those types

To drive <a car>
To eat <a fruit>
Enter fullscreen mode Exit fullscreen mode

So we can do this

To drive 🚙
To eat 🍋
Enter fullscreen mode Exit fullscreen mode

But what happens if we confuse things

To drive 🍋
Enter fullscreen mode Exit fullscreen mode

What does it mean to drive fruit? What is the result of this operation?

TypeError: you can not drive fruits. Duh!
Enter fullscreen mode Exit fullscreen mode

The result is nonsense. Also, you can say that this is an undefined value in the same way as the result of division by zero is undefined in math. Humankind doesn't know the answer.

As you can see types and type errors are not computer specific thing. Types exist because of humans. Humans like to discover patterns, group objects by properties and later make conclusions about the whole group.

Now let's see if our ideas about types hold true in the computer world.

Cars: 🚙, 🚌, 🚜.     → Number
Fruits: 🍋, 🍐, 🍓.   → String

To drive 🚙            → To multiply numbers
To eat 🍋              → To concatenate strings
Enter fullscreen mode Exit fullscreen mode

Flow

"a" * 1
Cannot perform arithmetic operation because string [1] is not a number.
Enter fullscreen mode Exit fullscreen mode

TypeScript

"a" * 1
The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type.
Enter fullscreen mode Exit fullscreen mode

Reason

"a" * 1
Line 1, 8: This expression has type string but an expression was expected of type int
Enter fullscreen mode Exit fullscreen mode

JavaScript

"a" * 1
NaN
Enter fullscreen mode Exit fullscreen mode

NaN stands for not a number. This is how IEEE (Institute of Electrical and Electronics Engineers) calls nonsense values for arithmetic operations.

NaN and how to handle errors

There are two ways to handle errors from a machine point of view:

  1. Raise exception. CPU will stop the execution of current instructions and jump to error handling function

  2. Return special value which represents an error. CPU will continue to execute current instructions

This special nonsense value is tricky because you can not do anything with it

nonsense + 1 = ? (nonsense)
nonsense * 1 = ? (nonsense)
nonsense / 1 = ? (nonsense)
Enter fullscreen mode Exit fullscreen mode

As soon you get one value somewhere in the middle of a calculation, it will manifest to the end of the calculation. This is also called toxic value 💀. Once it gets into the system everything is poisoned.

Those values hard to debug, because the result of the error can be found far away from the place where the error happened and there are no traces left. This is why it is highly discouraged to use it.

What is type checking?

The answer is trivial - this is when you check that given thing is a member of the collection or not, to prevent nonsense errors, like applying an operation to the wrong type value.

Type checking "performed by a system"

undefined()
VM180:1 Uncaught TypeError: undefined is not a function
    at <anonymous>:1:1
Enter fullscreen mode Exit fullscreen mode

Type checking "performed by a developer"

if (typeof x === "undefined") {}
Enter fullscreen mode Exit fullscreen mode

Dynamic type checking or type checking at runtime

undefined()
VM180:1 Uncaught TypeError: undefined is not a function
    at <anonymous>:1:1
Enter fullscreen mode Exit fullscreen mode

Static type checking or type checking before runtime

// @flow
undefined()
   ^ Cannot call `undefined` because undefined [1] is not a function.
Enter fullscreen mode Exit fullscreen mode

This post is part of the series. Follow me on twitter and github.

Discussion (4)

Collapse
jvanbruegge profile image
Jan van Brügge

Not sure why you avoided the term set, it's exactly what a type is, the set of all possible values. For example a union type in Typescript can be expressed as:

type A = number | string;
//same as
number = { n | n ∈ ℝ }
string = { s | s ∈ ∑* }
A = number ∪ string

Systems like LiquidHaskell even allow you to specify a subset of types for your functions, so you can e.g. only pass lists that are proven to not be empty to a function that extracts the first element.

Collapse
stereobooster profile image
stereobooster Author

The issue here is that sets have formal definition and if you say that types are sets you claim that types follow all formal rules for sets. I am not sure that this is the case, even that "simple" types are very similar to sets. I will need to read more to explain exact difference

Collapse
polentino911 profile image
Diego Casella

That's why I heartfelt aversion for any dynamic-typed programming language:
why would you throw away such a beautiful feature that does tell you, at compile time, if "X can be fitted into Y / passed to function Z()" (not just checking for undefined), rather than have errors at runtime ? :confused:

Lack of type checking looks great at first. But in the long run...

Collapse
emad__elsaid profile image
Emad Elsaid

Finally posted it :D, I like your writing style, keep it coming man