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

Richard introduces how choosing whether or not to expose a custom type's variants from a module affects the guarantees you can enforce about that type.

Get Unlimited Access Now

Transcript from the "Module Boundaries" Lesson

>> Richard Feldman: So on the big tapes, we're gonna talk about four things. First is the concept of module boundaries. Second is opaque types themselves. Third is validated data. And fourth is when not to go opaque. Okay. Let's start by talking about module boundaries. So here is a type, a custom type we've created.

[00:00:19] It says type email equals valid email string. The comments says th is guaranteed to be a valid email address which s a bold claim to make considering there is no validation going on here whatsoever. Is there someway we can make that comment true? Well there is from a module perspective.

[00:00:35] But not if we do this. So if this is our module heading this is module email exposing email parenthesis dot dot double parenthesis dot dot right there means that we can't actually make any guarantees about how this thing is constructed. The (..) means that we are exposing this ValidEmail variant.

[00:00:52] We're saying that even though this is our only variant, any module that imports the email module can construct one of these themselves if they want to. Not only that but they can pattern match on it. They can look into it, see what it's got, modify it and make a new one.

[00:01:05] So this claim that this is guaranteed to be a valid email address just can't possibly hold as long as we're exposing this variant. Okay, but let's say that we wanted to do a little bit better and we wanted to make it so that we could actually enforce this in variant.

[00:01:19] The way we will do this is by not exposing that variant. We would say, this is going to expose email type which means that other modules can import it so they can use it in their annotations. But we have not expose this ValidEmail variant. Nobody else can do that, only within this module can this thing can be instantiated.

[00:01:37] And then we can expose various ways to obtain one of these email values in a different without constructing it directly. So we can say, we'll have a fromString function, which takes a string and then returns a result with either a string error, just describing what went wrong, ie this email address doesn't have an at sign in it so it's not valid.

[00:01:55] It's missing the domain, so it's not valid, some description of what went wrong. And then in the event that it did successfully get converted from a string to a valid email, then we provide you with an email. And then of course we could offer a toString to let you go back from an Email to a String.

[00:02:09] Because that's an important thing to be able to do when you're rendering things on the screen. But with this API, because of this module boundary and the fact that we're not exposing this one variant that it has. We've made it so that it's not possible for someone to obtain an email value in any other module, without having gone through this fromString function, without having run through this validation that ends up giving us an email only if the validation passed.

[00:02:36] And now the comment's actually accurate. It really is guaranteed to be valid email address because if you've obtained one, you must have obtained it through this validation function.