Exposing union type without its tags, why?

Regarding the following code,

module Species exposing(Mammal)

type Mammal
    = Cat
    | Dog

I’ve got a newbie(stupid) question: I found the first line module Species exposing(Mammal) is valid. But what is it good for, instead of module Species exposing(Mammal(..))?

Exposing Mammal in the former way still cannot allow you access the contructor(Cat or Dog) from other modules. Then when it can be used?

It’s called a opaque type. You can think of a normal type as a class with only public fields and an opaque type as a class with only private fields.

A good example would be a sorted list: You want to ensure that the list stays sorted whenever you insert some new element. (See link for more infos)


In another thread I gave an example of a Length that assured it was never negative. It’s not a great example though since I was really answering a different question and just explaining opaque types as an aside.

1 Like

Another example might be if you want to be able to change the implementation of something, but keep its API the same. So the package elm/core exposed Dict as:

type Dict k v

Inside that package is an implementation of Dict. Maybe later someone decides a different data structure + algorithm is better (faster or uses less memory etc). With the opaque type, all of that can be changed behind the scenes without the users of Dict even being aware - they just plug in the new one and get the benefits.

So the idea comes from modular design principles, which state that you should give the smallest interface onto something that is needed to use it. This allows the complexity behind something to be hidden, which means that the user of it needs to understand less about it in order to use it. It also removes the possibility of a user accidentally making their code depend on some implementation detail, which would then cause their code to break if that is changed. So its about simplicity, and allowing developers to work in parallel by only having a shared API contract between them.

Of course if the API changes, Elm’s enforced semantic versioning will help to handle that too…


It’s one of my favorite features in Elm :blush:
It’s a feature that when used well can make your project a lot more maintainable, so it’s one worth learning more about it.

Dillon and I talked extensively on this topic on the Elm Radio podcast, if that’s a medium you like.


Thank you guys so much. I need more time to get a grasp on type(and opaque type) in a rush on learning elm. It’s a little like class in Java or even javascript, but the difference is huge.

There is some similarity - like a class with private fields, and private methods, that exposes how it works through its public methods only. I tend to think that a module in Elm is roughly equivalent to a class in Java.

I think both ideas have their roots in modular design principles.

A major difference is that OO languages have sub-classing, where you can always extend things. An Elm module supports only 1 implementation. You can make an even more class-like thing in Elm using records or extensible records, but its not a good idea to get into that without a compelling reason.


Understood. I want to be flat and shallow. OO make me thick and deep, which bring me admiration from newcomers but hates from users and maintainers.

So I turn to FP several years ago. Then I found Elm(fit me so well though a bit late)

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.