As tools and methodologies for software development have evolved, the functional programming paradigm has washed over the tools we use across the stack. Haskell has been a go-to tool for developers to use for data analysis for close to three decades. On the other end, languages like TypeScript and Elm have hit the front-end landscape like a wave. Thus allowing developers to develop type-safe applications that compile to browser-readable JavaScript. You've probably heard functional programming advocates discussing all the benefits of these tools and what you can do by putting more information into a type system. And well, they're right. On the front end, if you look closely, you can see how languages like Elm influenced the architecture libraries like Redux.

Statically-typed programming languages give you more control over how you build software.

As a note, I'm not going to spend time covering basic types and primitives here. Understanding higher kinded and dynamically kinded types requires a solid understanding of Haskell's underlying basic type system and their primitives. If you don't have that foundation, this may not be the right article for you. It may end up being more confusing than helpful.

### The Higher Kinds

As a quick refresher, at its core, a type is a way of classifying *things*. We assign types to functions and use them to compare a function's declared type and the values it returns. So, how do we define types in Haskell? If you're not already aware, a value constructor accepts a value and yields a value. In a similar manner, a type constructor accepts a type and yields a type.

```
data Maybe a
= Just a
| Nothing
```

Above, we declare a type **Maybe** that consists of two data constructors. **Just**, which accepts a value of type a, and **Nothing**, which does not accept any values at all. However, before we go any further, let's discuss those data constructors a bit more in depth.

```
ghci> :t Just
Just :: a -> Maybe a
ghci> :t Nothing
Nothing :: Maybe a
```

**Just** has a function type. This means that any value we give it will become a **Maybe** of that type. **Nothing**, however, can conjure up whatever type it wants without needing a value at all. This pattern is actually pretty common, and when we declare a type `Nothing :: Maybe a`

, what we're actually saying is that **Nothing** is. value of **Maybe a** for any and all types **a** that may be provided to it.

Armed with that knowledge, you might be driven to ask: what is the type of **Maybe**?

```
ghci> :t Maybe
\<interactive\>:1:1: error:
• Data constructor not in scope: Maybe
• Perhaps you meant variable ‘maybe’ (imported from Prelude)
```

Hold on a second. What's going on here? Does this mean that types don't have types of their own? Well, sort of. Types have *kinds*.

```
ghci> :Kind Maybe
Maybe :: * -> *
```

What? What is *****? ***** is the *kind* of types which have values. At its core, what this means is that the *kind* of **Maybe** accepts a type that has values. This is called higher kinded polymorphism. As a generalization, higher-minded polymorphism is a method for abstracting types from values. It's analogous to how functions already abstract values from values.

```
Data HigherKinded a b
= Bare b
| Wrapped (a b)
```

Haskell's kind inference is fairly simple and straightforward. If you scroll back up to the top, you'll see the similarities between it and Haskell's type inference. This is because when we declare a type that applies a function **a** to a value **b**, Haskell knows that **a** must have the kind `* -> *`

, and a type of *kind* ***** that returns a type of *kind* *****. In English, this means that `HigherKinded`

accepts two types: the first of which is a function that does not have values itself, but when provided with a type that *does* have values, those values are then applied to the function. The second argument is simply a type that have values. Finally, it returns a type that can have ordinary values. *Think about how the Maybe type's kind would be defined in this way.*

### Dynamically Kinded Programming

Moving on from higher kinded types, let's take a moment to discuss its lesser-known counterpart. The easiest way to discuss dynamically kinded types is through example.

```
ghci> data Void
ghci> :kind Void
Void :: *
```

When we declare a type **Void** and a subsequently check for that type's kind, we're greeted by *****. Are you surprised? In the same way that you can productively program at the value level with dynamic types as you learn how to do when building a foundation in functional programming, you can also program at the type level with dynamic kinds.

*At its core, that's what * represents*.

Now that you understand that, we can start encoding our first type level numbers. As an example, we can start by defining types for Peano natural numbers. Defining our numbers will be as easy as defining two data constructors, **Zero** and the **Succ** (Successor) of some natural number.

```
data Zero
data Succ a
type One = Succ Zero
type Two = Succ One
type Three = Succ Two
type Four = Succ (Succ (Succ (Succ Zero)))
```

As you can see from the example above, declaring our number types is as easy as using functional application with the two data constructors that we defined above. When defining subsequent types such as **Two**, or **Three**, you can observe that we can either reference already defined types to define the new type, or we can use functional application as in the case of the **Four** type. However, this method isn't without its caveats. There's nothing preventing us from declaring a type with `type One = Succ Bool`

, which doesn't make any sense. In order to get around this, we'll need to introduce more kinds than the mere ***** kind that we've been dealing with until this point.

### Data Kinds

The `DataKinds`

GHC extension allows us to extend the functionality of data constructors into type constructors. As a consequence this also extends the type constructors into kind constructors. Just as with the above information, the best way to convey what this extension allows you to do is through example.

```
{-# LANGUAGE DataKinds #-}
data Nat = Zero | Succ Nat
```

The definition above declares a new type **Nat** that consists of two value constructors, **Zero** and **Succ**. The `DataKinds`

extension introduced a new kind **Nat**, which exists in a separate namespace. In addition to this, we also get two new types, **'Zero**, which has the kind of **Nat**, and **'Succ**, which accepts a type of kind **Nat**.

```
ghci> :kind 'Zero
'Zero :: Nat
ghci> :kind 'Succ
'Succ :: Nat -> Nat
```

Fairly simple, right? And as an aside, If we ever need to promote something a level up, we prefix the name with an apostrophe: **'**. Where it can be ambiguous, the **'** is used to disambiguate. Otherwise, Haskell will infer which you mean. After all, if we check the types, they should look the same!

```
ghci> :type Zero
Zero :: Nat
ghci> :type Succ
Succ :: Nat -> Nat
```

And that's all that there is to it when it comes to extending our type and kind constructors. We now have the ability to architect pretty basic types and kinds. However, in order to actually use them, though, we'll need to dive in a little deeper.