Understanding haskell's type system can be challenging coming from other programming language. In this blog posts i'm gonna draw some parallels between haskell and php. This is my own interpretation and not neccesarly 100% correct. Everything will be colored so that it will be clear if it's about haskell or php.

What is important to know is that the CPU does not get to work with the type system of a language. By the time haskell or php code is turned into cpu instructions all typing is gone. Therefor it depends on how smart the compiler is about turning the data of a type into data for cpu instructions. This gets important as you might be resistant to using many types, but you can use them plenty and optimize later if needed.

Where php has a clear distinction between kind of data: classes (objects), string, int, etc, haskell has a more uniform way of doing things. Meaning that in php syntax it's a big deal if you want to make a new string or a new object, where as in haskell there is just one way to do data, any data.

Haskell kind of data are like classes in php. Let's pretend php has only classes, to represent a bool one would have to make classes like this:

class True { }
class False { }

This is not really valid php because True and False are reserved keywords. But let's take a look a these classes anyway. They don't have any properties (or methods), but they are still usuable to represent true or false. To check if something is true:

$arg instanceof True

You might consider php classes to be types of objects, and thus classes are types. And thought earlier when written Haskell kind of data why not just name them types? To do that would be to understand everything so far as being in the "php paradigm", but we want to understand things as they are in the "haskell paradigm". So here comes the take away point of this blog post.

If you have a class True and a class False. The fact that you have two different classes is already a piece of information, and therefor this is data on which you can base your functionality. So looking from php classes can be types, but actually they can serve as data too. And in haskell they are only that, just data. In fact types in haskell are something entirely different as we will see in the next section.

data

With data you can make your own types in haskell. Let's look at how to define boolean:

data Bool = False | True

So if True and False are like classes, what's Bool? Bool is like an abstract class, one that can not be instantiated but forms a commonality between it's child classes. In php this would look like:

abstract class Bool {}
class True extends Bool {}
class False extends Bool {}

This abstract class is called a type in haskell.

Now to check if something is of type Bool you can use:

$arg instanceof Bool

In php there is no more syntactical distinction between data and types when using classes for everything. Again .. this is not the way to do php obviously. But let's see how haskell makes this distinction.

In haskell there is no clear visual distiction between data (True and False) and type Bool. They both start with a capital and there is no special syntax like the new keyword in php. You will just have to remember that the thing on the left of = is about types and the thing on the right of = is about data. Though the compiler will know the difference, so you can not give a type when a piece of data is expected and the other way around.

data constructor

Just as in php, where classes (objects) can take on extra payload, haskell can do this too. First let's consider this class in php

class Circle {
  public $diameter;

  public function __construct(int $diameter) {
    $this->diameter = $diameter;
  }
}

Note: type hinting of int is available in php 7.

In haskell this would look like

data Shapes = Circle Int

Now everywhere you want to have a Circle you have to put Circle and give it an int like so:

a = Circle 5

Circle does kind of look like a function, but it's not, it's called a data constructor. It's part of the data, just like you can do:

a = True

Where True doesn't need any additional payload (called a field).

Here is another difference between php and haskell. In php the term constructor is used to refer to a function, where as in haskell the name constructor is used for the first part of the data and after it comes the rest of the data (the payload). Why are those both called constructor then? Well they are both used in the process of making new data. Just in an entirely different way, so you can definitely not think of haskell constructors as just being a function, thinking of them as data itself makes more sense. (Actually they are functions, but you probably only need to understand that later)

Notice that specifying a type (which would be like an abstract class in php) for Circle is mandatory, and in this example we named it Shape. Often enough a group consist only out of one possible piece of data. In that case you can do

data Circle = Circle Int

Which will be accepted by the compiler but can get very confusing when using Circle somewhere. You can prefix either the type constructor or the data constructor to make a distinction. Prefixing the data constructor with Mk is often used:

data Circle = MkCircle Int

type constructor

I briefly mention the type constructor here because it's often named together with the data constructor. However it's use has not much to do with "just creating your own type", in fact this is a more advanced scenario where you need polymorphism.

Ok i lied, you were already using the type constructor. In:

data Bool = False | True

Bool is not just the name of a type but a different sort of constructor: the type constructor. It's use is to help making your data payload be of different kinds. Let's say you have a Circle. But you don't know up front if you want to express that as Int or Float. In that case you can do:

data Circle a = MkCircle a

Where a is called a type parameter.

newtype

newtype can be seen as a more optimized version of data but it has a more specialized use case.

data Shapes = Circle Int | Rectangle Int

Where data has to check at runtime is this a Circle or is it a Rectangle, newtype doesn't have this runtime overhead. Simply because it can only have one data constructor which must have one field.

newtype Circle = MkCircle Int

It basically serves and alias for Int but you get type checking (rather then just accepting ANY int).

type

All this explicit typing does come at a cost, actually having to check the type. All functions you use should have special logic for your specific type. That means if you have a string used to represent a name and you do:

newtype Name = MkName String

Then if you want to take the first two characters of your name you can not do:

take 2 (MkName "John")

Either you would have to unwrap Name and loose the typing information, like so:

getName (MkName a) = a
take 2 (getName (MkName "John"))

or if your newtype derives Functor

fmap (take 2) (MkName "John")

The point is not to understand Functor or fmap here, it's just a demonstration of the extra work involved when working with your own defined types.

So if you just want a visual indication (and no type checking) that your function takes a name you can do:

type Name = String

upperName :: Name -> Name
upperName name = map Char.toUpper name

Instead of having type

upperName :: String -> String

Again this serves nothing more other than making your code looks nice and more readible.