improve opaque types intro

Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com>
This commit is contained in:
Anton-4 2023-02-07 13:39:05 +01:00 committed by GitHub
parent 24591b9ea0
commit 6bcca92578
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1008,9 +1008,12 @@ Here, Roc's compiler will infer that `color`'s type is `[Red, Yellow, Green]`, b
### [Opaque Types](#opaque-types) {#opaque-types}
Roc enables information hiding through an *opaque type* language feature. To define an opaque type you can use the `:=` operator. This type can then be exported for use elsewhere, however, its internal implementation is only visible within the same scope where the type is defined. This is similar to Elm's [opaque types](http://sporto.github.io/elm-patterns/advanced/opaque-types.html).
A type can be defined to be opaque to hide its internal structure. This is a lot more amazing than it may seem. It can make your code more modular, robust, and easier to read:
- If a type is opaque you can modify its internal structure and be certain that no dependencies need to be updated.
- You can prevent that data needs to be checked multiple times. For example, you can create an opaque `NonEmptyList` from a `List` after you've checked it. Now all functions that you pass this `NonEmptyList` to do not need to handle the empty list case.
- Having the type `Username` in a type signature gives you more context compared to `Str`. Even if the `Username` is an opaque type for `Str`.
Lets see how to create an opaque type; suppose we define the following inside the `Username` module:
You can create an opaque type with the `:=` operator. Let's make one called `Username`:
<pre><samp>Username <span class="colon">:=</span> Str
@ -1023,9 +1026,9 @@ toStr <span class="kw">=</span> <span class="kw">\</span>@Username str <span cla
str
</pre></samp>
Here, `Username` is an opaque type. The `fromStr` function turns a string into a `Username` by calling `@Username` on that string. The `toStr` function turns a `Username` back into a string by pattern matching `@Username str` to unwrap the string from the `Username` opaque type.
The `fromStr` function turns a string into a `Username` by calling `@Username` on that string. The `toStr` function turns a `Username` back into a string by pattern matching `@Username str` to unwrap the string from the `Username` opaque type.
Now we can expose the `Username` opaque type we defined so that other modules can use it in type annotations. However, other modules can't use the `@Username` syntax to wrap or unwrap `Username` values. That operation is only available in the same scope where `Username` itself was defined; trying to use it outside that scope will give an error.
Now we can expose the `Username` opaque type so that other modules can use it in type annotations. However, other modules can't use the `@Username` syntax to wrap or unwrap `Username` values. That operation is only available in the same scope where `Username` itself was defined; trying to use it outside that scope will give an error.
Note that if we define `Username := Str` inside another module (e.g. `Main`) and also use `@Username`, this will compile, however the new `Username` type in main would not be equal to the one defined in the `Username` module. Although both opaque types have the name `Username`, they were defined in different modules and so they are type-incompatible with each other, and even attempting to use `==` to compare them would be a type mismatch.