# Structural, Nominal, and Duck typing

Structural, Nominal, or Duck typing are the different methods by which Type Systems compares the compatibility and equivalence of data types. In this tutorial, let us dig into this and find the differences and similarities between them.

## Type Compatibility

“Type compatibility” refers to the similarity of two types to each other. One of the important tasks of a type system is to determine if the two given types are compatible with each other or if a type is a subtype of another type.

Type Compatibility is important because if a type is compatible with another type, then you can convert it and substitute it in operations involving another type.

Consider the primitive data types `integer` and `decimal`. We can convert an integer value to a decimal value without losing any precision. We can easily substitute integer values everywhere a program expects a decimal type. Hence, we can say that the integer is compatible with the decimal data type.

Type T1 compatibility with T2 does not mean that T2 is also compatible with T1. An integer is compatible with a decimal, but a decimal type is definitely not compatible with an integer. Converting a decimal value into an integer definitely results in a loss of precision. Hence, they are not compatible.

This C# example demonstrates the above. The code contains two functions `addDec` & `addInt`. We can pass an integer to a `addDec` function, but the compiler throws an error if we try to pass `decimal` to `addInt` method

It is easier to compare the primitive types like integer, string, decimal, etc. They have a simple structure. But objects, classes, etc., have complex structures. There are two ways in which type systems compare the types to each other for compatibility. One is nominal typing and the other one is structural typing.

1. Nominal typing uses the name to compare types
2. Structural typing uses the structure to compare types.

Duck typing is usage-based structural equivalence, which we usually find in dynamic languages which do not have a strong typing

## Nominal Typing

The nominal systems determine compatibility by explicit declarations and/or the names of their types. Each type is unique in the nominal system. Even if they have the same data and shape, we cannot assign them across types.

For the types to be compatible in a nominal system

1. type name must match or
2. we explicitly declare the type as a subtype of another type. Here the type is compatible with its parent type.

The best examples of nominal type systems are C# & Java.

Take the following `Dog` and `Cat` class is written in C#. Note that both the objects have `name` property and `makeNoise` method.

We create two objects, a dog and a cat, from the types Dog and Cat Respectfully.

The MakeNoise function takes a Dog and invokes makeNoise method

It will work with a dog object, but not with a cat object. The C# compiler throws the error. cannot covert from Cat to Dog. Although the shape of both objects is the same, C# treats them as incompatible with each other. Hence, we cannot substitute a cat for a dog.

This is how the nominal type systems work. The nominal system always treats the objects are incompatible if it created from different types (unless they have inheritance relationship).

### Nominal Typing with Subtypes

We can also create a type as a subtype of another type. This is also known as inheritance. The inherited type is always compatible with its Parent. But Parent is not compatible with their child

Let us create a subtype `BullDog` by extending the `Dog` type.

Now, you can use the `bulldog` as a replacement for `Dog` type. The `makeNoise` method will accept `bulldog` object.

Now, update `makeNoise` method to accept `BullDog` instead of `Dog`.

Now, we cannot pass `Dog` to `makeNoise`. We cannot substitute types with their super type or parent type.

## Structural Typing

In structural typing, a type is considered compatible with a supertype if it has all the members of the supertype and, optionally, additional members. Here, the shape of the type is more important than its name.

TypeScript uses structural typing to check for equivalence.

The following is the Typescript equivalent of the C# code from the previous section.

The example has two types `Dog` and `Cat`. Both these types have the same structure. We create a `dog` and `cat` instance and invoke `makeNoise`. Although the `makeNoise` expects a `dog` instance, it does not complain when we pass a `cat` instance. This is because Typescript will accept any object that has a shape that is the same as that of the `Dog` class. How we create the instance is immaterial in the structural typing type system.

In this example, the `Person` class does have a `makeNoise` method. But it does not have `name` property. Hence its shape is not the same as that of the `Dog` class. Invoking `makeNoise` with `Person` results in a compiler error.

The code works even if the `Person` class has additional property. As long as the Person has `name` and `makeNoise` property it works.

You can pass any random object as long it has `name` and `makeNoise` method

## Duck Typing

Duck typing neither cares about the name nor the structure of the type. It must have the given method or properties required by the operation. We find duck typing only in dynamically typed languages like JavaScript.

The duck type originates from the phrase “If it walks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck“. It means that if an entity behaves like a duck then you can safely assume it is probably a duck. In the type system, if you are expecting a certain behavior and the object has those behaviors then you can use that object. Its shape and type are not important.

This JavaScript example contains two objects `person` and a `bankAccount`. They neither share the same type nor have the same structure. But they have one common method `someFn`. The `invokeSomeFn` functions accept both the objects without any issue. In fact, you can pass anything to `invokeSomeFn` as long as that type has `someFn` method.

If the object does not have the `makeNoise` JavaScript simply throws the TypeError.

## Nominal vs Structural vs Duck

Duck typing is the most flexible while nominal typing is the least flexible. But highly error-prone and bugs are difficult to track.

In the Nominal typing system, we need to specify the type of the data explicitly or implicitly. This is more code, but easily readable code and offers less flexibility. The Nominal systems are less error-prone and bugs are easy to find and fix.

In structural typing, we do not need to specify the type. it offers more flexibility than a nominal typing system but does not stop you from passing the wrong object to an operation. Such bugs are difficult to track.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top