Check out a free preview of the full Advanced Elm course:
The "Opaque Types" Lesson is part of the full, Advanced Elm course featured in this preview video. Here's what you'd learn in this lesson:

Opaque types are types whose implementation details are hidden from other modules. Richard gives examples of opaque types in core Elm libraries, only some of which are custom types under the hood.

Get Unlimited Access Now

Transcript from the "Opaque Types" Lesson

>> Richard Feldman: So fundamentally, this is how we can, as Elm programmers, construct guarantees like this. So if another module attempts to do this anyway, and they say, you know what? I don't care about your alleged guarantee. I'm going to import this email thing anyway, and I'm going to construct an invalid email just to spite you.

[00:00:18] Well, it's just not gonna compile. Because if the variance is not exposed, you can't access it from other modules. So this is how we create what's known as an Opaque Type. We define a type, and it doesn't even have to have any particular data associated with it. We could literally say type Email = Email, the most boring type you can imagine.

[00:00:41] But if we're not exposing that variant, that is an opaque type. The opacity that it refers to means that other modules can't look inside of it. They can see that they have one, but that's all they can see. In other words, the implementation details are hidden within the module where that custom type is defined.

>> Richard Feldman: Some other examples of opaque types, Random.Generator from the core random library. Decoder, like JSON decoder is an opaque type, and HTML is an opaque type. Now one of the interesting things about opaque types is that this is not the only way to get them. So these things right here, Decoder and Html, those in particular are built using kernel code, underlying root level system stuff inside of Elm.

[00:01:25] The virtual DOM is baked into Elm. Html is an opaque type, but its internals are not custom type. It's not just a series of variants. It's something more complicated than that that relies on deep internals within Elm. But the point is that we don't know that, and we can't know that.

[00:01:42] We can't rely on that. We can't rely on any of the internal implementation details of Html, of decoder, of random.generator or even of this email type because it's all confined to that one module. So the key thing here is not so much that we've used a custom type, but not exposed the constructor.

[00:01:58] But rather that the opacity of the type is what other modules can do with that type, once they've got one. And specifically, all they can do if it's opaque, is they can annotate that they've got one. That's it. They can hold onto it. They can't look inside. They can't make a new one.

[00:02:14] Questions about that? Yeah?
>> Speaker 2: Is there an example of implementation? When you say implementation details are in, for Html I guess it would be like how does it turn into Html, right?
>> Richard Feldman: Sure, yeah.
>> Speaker 2: Okay.
>> Richard Feldman: Yeah, exactly, I mean, basically you can't know the structure, might be another way of saying it.

[00:02:36] You can just see the outside shape or you don't know what is going on under the hood. Right, you can modify it, so in an immutable language or whenever you have immutable values, modify means look at and create a new one, right? And you can't do either of those.

[00:02:51] [LAUGH] You can observe that you have one but not what's going on inside it. This is also relevant for your ability to evolve your code over time without causing regressions. Because if you know that nobody can rely on your implementation details, if later on you wanna change those implementation details to add a performance optimization or to just refractor your code, you know that it's safe to do that.

[00:03:11] Because nobody else could possibly be relying on those and have their code get broken by them. Even if it still type checks. Good question, other questions? Yeah.
>> Speaker 3: So in this specific example, the implementation detail is just the ability to construct.
>> Richard Feldman: Right, yeah, so this is the dumbest possible example because we actually don't have any.

[00:03:32] As it happens, we've hidden the implementation details, and the implementation details are completely boring. The deep dark secret of this email type is that it does nothing. But if we did have other stuff in here, you still wouldn't be able to tell. This could be Email string, it could be Email string int foo bar.

[00:03:48] No one knows outside this module.