Pipeline builder

This is a common pattern used for decoders and validation. This pattern is used to build a function for processing some data using a series of piped functions.

type alias User =
	{ name: String
	, age: Int

validateUser : User -> Result String User
validateUser user =
    Ok User
        |> validateName user.name
        |> validateAge user.age

This builds a function validateUser that will take a user and validate it. This validateUser function works like the railway pattern. We might get an Ok User at the end or an error Err String.

This pattern relies on the fact that a type alias in Elm can be used as a function. e.g. User is a function like:

String -> Int -> User

We start by putting the function (User) into a Result.

Then each function in the chain takes an attribute and the previous result, does the validation and returns a result back.

validateName : String -> Result String (String -> a) -> Result String a
validateName name =
        (\constructor ->
            if String.isEmpty name then
                Err "Invalid name"

                Ok (constructor name)

Complete example https://ellie-app.com/9SZTHJqB5r2a1


When using this pattern we have to be careful with the order of functions in the pipeline. It is easy to make a mistake when the end type has many attribute of the same type.

type alias User =
	{ name: String
	, email: String

With this type, we can mix up the order of name validation and email validation e.g.

    Ok User
        |> validateEmail user.email
        |> validateName user.name

This will work, but it will give us a User with the attributes mixed up:

{ name = "sam@sample.com"
, email = "Sam"

Some example packages using this: