Designing Opaque Type for form fields in Elm: Part 2
Tags: elm, architecture, webdev
Published: 2020-04-12 12:35:59

---
title: Designing Opaque Type for form fields in Elm: Part 2
published: true
description: How to make your module more extensible with module composition.
tags: elm, architecture, webdev
series: Designing Opaque Type for form fields in Elm
---

In the previous article, we implemented `Username` module which encapsulates validation logic of form fields.

That's really enough at that moment, but it still has more room to get improved in viewpoint of extensibility. 

Say, if we would like to implement one more another field of `Email` that have almost the same logic of `Username`, but with different validation rule. At some point, we can follow rules of **WET (Write Everything Twice)**. Abstract something out too early is always a way to ruin your application. So, it would be better to be dumb about it just by cloning functions of `Username` into `Email` module. Then, module relation will be as follows.

![Module relation diagram](https://dev-to-uploads.s3.amazonaws.com/i/212z8wiac1iob2us2t3h.png)

It looks fine for now, but getting stinky. Being WET is nice, but don't always be wet. As solution toward this kind of case, we can take advantage of **module composition** in order to make modules highly coherent and reusable without breaking border of responsibility each module has. This is really similar to class inheritance, but way better than that.

# Explicitness and Implicitness

Elm does not have class-based syntax so that naturally we always will take step to use module composition to extend behavior of one another module. This is a nice thing that we don't have any chance to get into pitfalls of class inheritance.

Class inheritance is usually seemed to be **implicit**, because the all public behaviour of parent classes is carried over by a child class, sometimes intentionally, but sometimes not. This means class inhertance is not suitable to pick only some specfic behaviours of other classes.

Composition seems way **explicit** on the contrary. If we don't have to inherit some specific functionality, just ignore it. It even has chance to inherit behaviours of compositing classes just by delegation. Simple and concise.

Using module composition in Elm is a key to make your modules extensible and reusable all the way. Extracting business rules in your application into Opaque Type is nice to decouple interface and implementation. Module composition can empower it to be reusable in a good manner.

# Overall design

Now, let's apply module composition into our application.

![overall design](https://dev-to-uploads.s3.amazonaws.com/i/bvu60vv8vyq16x41nlx5.png)

Right before using composition, each module had its own implementation intendedly duplicated. However, now, their internal implementations are delegated to `Field` module that has fundamental implementation abstracted out of `Username` and `Email`.

# Field module

`Field` module now has three states initially `Username` had before.

`validator` field in `Common` record is important. That is defined as a function interface that gets injected in `init` function. The reason why `validator` interface requires implementation to return `( String, err )` tuple is that, even though validation fails, the current value is needed to be accessible to show it on input field. If it has only `err`, the value typed by users will be lost. This must not.

```elm
module Field exposing (Field, init)


type alias Common err =
    { value : String
    , validator : String -> Result ( String, err ) String
    }


type Field err
    = Partial (Common err)
    | Valid (Common err)
    | Invalid (Common err) err


init : String -> (String -> Result ( String, err ) String) -> Field err
init value validator =
    Partial
        { value = value
        , validator = validator
        }
```

Of course, `Field` module has `input` function and `blur` function as well, but they are more abstract.

```elm
module Field exposing (Field, init, input, blur, mapErrorToString)

import Html exposing (Html)
import Html.Attributes exposing (type_, value)
import Html.Events exposing (onBlur, onInput)


-- ...

input : (Field err -> msg) -> msg -> Field err -> Html msg
input onInputMsg onBlurMsg field =
    let
        onInputHandler =
            \value ->
                onInputMsg <|
                    case field of
                        Partial { validator } ->
                            Partial
                                { value = value
                                , validator = validator
                                }

                        Valid { validator } ->
                            value
                                |> validator
                                |> mapResult field

                        Invalid { validator } _ ->
                            value
                                |> validator
                                |> mapResult field
    in
    Html.input
        [ type_ "text"
        , value <| toString field
        , onBlur onBlurMsg
        , onInput onInputHandler
        ]
        []


blur : Field err -> Field err
blur field =
    case field of
        Partial { value, validator } ->
            value
                |> validator
                |> mapResult field

        _ ->
            field


mapErrorToString : (err -> String) -> Field err -> Maybe String
mapErrorToString translator field =
    case field of
        Invalid _ err ->
            Just <| translator err

        _ ->
            Nothing
```

Some more internal functions

```elm
-- internals


mapResult : Field err -> Result ( String, err ) String -> Field err
mapResult field result =
    let
        validator_ =
            case field of
                Partial { validator } ->
                    validator

                Valid { validator } ->
                    validator

                Invalid { validator } _ ->
                    validator
    in
    case result of
        Ok value ->
            Valid
                { value = value
                , validator = validator_
                }

        Err ( value, err ) ->
            Invalid
                { value = value
                , validator = validator_
                }
                err


toString : Field err -> String
toString field =
    case field of
        Partial { value } ->
            value

        Valid { value } ->
            value

        Invalid { value } _ ->
            value
```

Next, let's look into how to use this `Field` module under the way of module composition in order to create specialized modules.

# Username module

As the way of module composition, almost all exposed function delegates its functionality to internal dependent module. `Username` module is just a wrapper of `Field` in this meaning that provides specialized behaviours extended from `Field` module.

One more great thing is that, **interface of `Username` module is almost not changed since the last article** even though its internal functionality is updated and being delegated! Actually, `errorString` function was newly introduced in place of `error` function that is described in the last article, but this is just an optional change. 

Everything stays same overall. The stability of module interface is always important to avoid unintentional effect to other functionality.

```elm
module Username exposing (Error(..), Username)

import Field


type Username
    = Username (Field.Field Error)


type Error
    = LengthTooLong
    | LengthTooShort


empty : Username
empty =
    Username <|
        Field.init "" <|
            \value ->
                if String.length value > 20 then
                    Err ( value, LengthTooLong )

                else if String.length value < 5 then
                    Err ( value, LengthTooShort )

                else
                    Ok value
```

`input` function and `blur` function are, as they exactly show, just does delagation to functions provided by `Field` module.

`errorString` is a little different. It works like a translator of `Error` type in this `Username` module. Error patterns vary on every module extensing `Field` so that `Username` module must have responsibility of translating error type to `String` message as a specialized module.

```elm
module Username exposing (Error(..), Username, blur, empty, errorString, input)

import Field
import Html exposing (Html)


-- ...

input : (Username -> msg) -> msg -> Username -> Html msg
input onInputMsg onBlurMsg (Username field) =
    Field.input (onInputMsg << Username) onBlurMsg field


blur : Username -> Username
blur (Username field) =
    Username <| Field.blur field


errorString : Username -> Maybe Error
errorString (Username field) =
    Field.mapErrorToString
        (\error ->
            case error of
                LengthTooShort ->
                    "Length is too short"

                LengthTooLong ->
                    "Length is too long"
        )
        field
```

This article has shown only the example of `Username` type, but with this strategy, it would be way easier to implement some similar types like `Email`, `Biography` that need validation like this. That's because essential logics to help us implement it as fields are now reusable.

Complete example is on Github.

{% github IzumiSy/elm-compositional-form-field %}