# Handling Nested Conditional Logic

**URL:** <https://discourse.elm-lang.org/t/handling-nested-conditional-logic/2163>\
**Category:** Learn\
**Created:** [October 8, 2018, 11:40am UTC](https://discourse.elm-lang.org/t/handling-nested-conditional-logic/2163 "2018-10-08T11:40:43Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![DylanLester](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dylanlester/32/1542_2.png) [@DylanLester](https://discourse.elm-lang.org/u/DylanLester)\
**Post date:** [October 8, 2018, 11:40am UTC](https://discourse.elm-lang.org/t/handling-nested-conditional-logic/2163/1 "2018-10-08T11:40:44Z")

</div>

Hi,

I’m new to Elm (and functional programming) and trying to learn by building a simple login page. I’m trying to build something similar to [Bitbucket’s login system](https://id.atlassian.com/login). This involves showing input validation and using progressive disclosure to reveal the password field.

If I were to represent this with a conditional construct it may look like this:

```javascript
// only show feedback on submission
if (form === submitted) {
  if (email === valid) {
    // show valid email feedback

    // show password field

    if (password === valid) {
      // successfully login
    } else {
      // show invalid password feedback
    }
  } else {
    // show invalid email feedback
  }
}

```

Should a conditional construct like above be used?

Can a custom type be used instead? In doing so, the `case ... of` expression still closely resembles the `if ... else` from above, is this better?

Or is there an altogether better solution?

* * *

Bitbucket Login: Valid email, password showing  
 ![image](https://cdck-file-uploads-canada1.s3.dualstack.ca-central-1.amazonaws.com/flex035/uploads/elm_lang/original/2X/a/ad0d2e20c5e583b32d71881ad8d4edd09bd7282d.png)

Thanks in advance!

---

<div class="post-metadata">

**Author:** ![andys8](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/andys8/32/280_2.png) [@andys8](https://discourse.elm-lang.org/u/andys8)\
**Post date:** [October 8, 2018, 12:47pm UTC](https://discourse.elm-lang.org/t/handling-nested-conditional-logic/2163/2 "2018-10-08T12:47:04Z")

</div>

Hey,

I think [`Result.andThen`](https://package.elm-lang.org/packages/elm/core/latest/Result#andThen) could be what you’re looking for. I’d go with multiple validation functions, that each return a `Result`. You can then chain them together with `andThen` and get an error or an `Ok` at the end.

For more, you can search for “elm validation result”.

There are also libraries like `elm-validate` and `elm-verify`, although I think you’re fine with a deeper look into `Result` 🙂

---

<div class="post-metadata">

**Author:** ![Qqwy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/qqwy/32/1062_2.png) [@Qqwy](https://discourse.elm-lang.org/u/Qqwy)\
**Post date:** [October 8, 2018, 9:24pm UTC](https://discourse.elm-lang.org/t/handling-nested-conditional-logic/2163/3 "2018-10-08T21:24:11Z")

</div>

In JS it already becomes difficult to read, which is why in many (especially non-functional) languages a common advice is to ‘fail fast’:

In semi-JS-esque pseudocode:

```auto
`if (form !== submitted) {
  return "Form not submitted!"
}
if (email !== valid) {
  return "Invalid email"
}
if (password !== valid) {
  return "Invalid password"
}

// stuff is valid! Do something useful!

```

In Elm, we can go one step further, and extract this pattern out into an `Either error result` (`Either` is better than `Maybe` because it allows you to clearly indicate _which_ failure occurred, which you definitely want in your example):

```auto
inputData
|> validateFormSubmitted
|> Either.andThen validateEmail
|> Either.andThen validatePassword
-- We now either have an `Err "Invalid Email!" or something similar, or an `Ok SuccessfulDataStructure`

```

---

<div class="post-metadata">

**Author:** ![koenusz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/koenusz/32/124_2.png) [@koenusz](https://discourse.elm-lang.org/u/koenusz)\
**Post date:** [October 9, 2018, 7:01am UTC](https://discourse.elm-lang.org/t/handling-nested-conditional-logic/2163/4 "2018-10-09T07:01:46Z")

</div>

I think [Making impossible states impossible](https://www.youtube.com/watch?v=IcgmSRJHu_8) applies here.

It would be nice to define a type like this and use pattern matching for your logic.

```auto
type alias Data = { email : String, password : String}

type Model = Filling Data | Submitted | EmailInvalid Data | PasswordInvalid Data

```

---

<div class="post-metadata">

**Author:** ![joelq](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joelq/32/445_2.png) [@joelq](https://discourse.elm-lang.org/u/joelq)\
**Post date:** [October 10, 2018, 10:20pm UTC](https://discourse.elm-lang.org/t/handling-nested-conditional-logic/2163/5 "2018-10-10T22:20:58Z")

</div>

## TL;DR

This turned out pretty long so let me summarize here. Read through the whole thing if you want to see the process of how I worked up to the final solution.

The answer to @DylanLester’s quesiton is **yes**. Using a custom type allows us to write flatter case statements that scale better as we add more fields. They also gives us more guarantees and allow us to have a richer data model. Here’s the final result:

```elm
type Model
  = EmailForm (Validated Email)
  | PasswordForm Email (Validated String)
  | LoggedInScreen Email

```

I used the techniques described below to build up a [full working example](https://ellie-app.com/3zzsjs5HtdQa1) on Ellie.

## Process

When faced with a problem like this I like to:

1. Enumerate the possible states of my program
2. See if I can represent them with a custom type
3. Find invalid states and eliminate them
4. Repeat until satisfied

For your problem, it looks like there are three screens - email form, password form, logged in screen. These can be represented as a custom type:

```elm
type Model
  = EmailForm
  | PasswordForm
  | LoggedInScreen

```

## Keeping track of user inputs

This is a good start but it’s incomplete. On the email form, we want to keep track of what the user is typing. Same thing on the password form. Let’s add parameters to for those to our type:

```elm
type Model
  = EmailForm String
  | PasswordForm String
  | LoggedInScreen

```

That’s better. However, we want to display the email from the email form on both the password form and logged in states. We’ll need to make sure we store it there too:

```elm
type Model
  = EmailForm String
  | PasswordForm String String
  | LoggedInScreen String

```

## Making impossible states impossible

That’s starting to cover most of our use-cases. What about invalid states? The user input on the email and password forms is allowed to be invalid so all values are permitted. However, we’d like to assume that our email is valid once we’ve moved past the email form step.

✅`EmailForm ""`  
✅ `EmailForm "notanemail"`  
✅ `EmailForm "valid@email.com"`

❌ `PasswordForm "" ""`  
❌ `PasswordForm "" "password"`  
❌ `PasswordForm "notanemail" ""`  
❌ `PasswordForm "notanemail" "password"`  
✅`PasswordForm "valid@email.com" ""`  
✅`PasswordForm "valid@email.com" "password"`

❌ `LoggedInScreen "notanemail"`  
❌ `LoggedInScreen ""`  
✅ `LoggedInScreen "valid@email.com"`

## Aside: Opaque types

To solve this problem, we can create an [opaque type](https://medium.com/@ckoster22/advanced-types-in-elm-opaque-types-ec5ec3b84ed2) whose only exposed constructor forces validation. With this approach, we can be sure that an `Email` value has a valid email:

```elm
module Email exposing (Email, fromString, toString)

type Email = Email String

-- this is the only way for code outside this module
-- to construct an email value

fromString : String -> Result String Email
fromString string =
  if String.contains "@" string then
    Ok (Email string)
  else
    Err "Email must contain an @ sign"

toString : Email -> String
toString (Email email) =
  email

```

## Putting it all together

Now that we have our opaque `Email` type, we can use it to eliminate invalid emails:

```elm
type Model
  = EmailForm String
  | PasswordForm Email String
  | LoggedInScreen Email

```

## Aside: Validation type

Our type is starting to look really good. There’s one thing it doesn’t store though. The validation state and possible error messages. We can use the same process we’ve used for the `Model` type to define a type that models validated data. We need three states: an initial empty state (so fields aren’t marked invalid on first page load), an invalid state, and a valid state.

```elm
type Validated
  = Initial
  | Invalid
  | Valid

```

For the invalid and valid cases, we want to track the raw string that was in the input so that we can re-render the form correctly. The initial state is always empty.

```elm
type Validated
  = Initial
  | Invalid String
  | Valid String

```

If the data is invalid, we’ll also want a string for the error.

```elm
type Validated
  = Initial
  | Invalid String String
  | Valid String

```

Finally as a nice-to-have, we might want to store a transformed valid string into a type that enforces correctness (like our `Email` from before).

```elm
type Validated a
  = Initial
  | Invalid String String
  | Valid String a

```

We might define a function like:

```elm
validate : (String -> Result String a) -> String -> Validated a
validate validationFunction raw =
  case validationFunction raw of
    Ok value -> Validated raw value
    Err error -> Invalid raw error

```

We could use it like:

```elm
validate Email.fromString "notanemail"
-- Invalid "notanemail" "Email must contain an @ sign"

validate Email.fromString "valid@email.com"
-- Valid "valid@email.com" (Email "valid@email.com")

```

## Putting it all together

Now that we have the validation type, we can wrap all user inputs in it to get validation status and errors:

```elm
type Model
  = EmailForm (Validated Email)
  | PasswordForm Email (Validated String)
  | LoggedInScreen Email

```

Looks like we now capture _all_ the states we want and _none_ of the states we don’t want! 🎉

## Rendering the view

So does this make the view code any nicer? Let’s take a try:

```elm
view : Model -> Html Msg
view model =
  case model of
    EmailForm userInput -> emailForm userInput
    PasswordForm email userInput -> passwordForm email userInput
    LoggedInScreen email -> loggedIn email

emailForm : Validated Email -> Html Msg
emailForm userInput =
  div []
    [ hint userInput
    , input [value (rawString userInput)] []
    ]

hint : Validated a -> Html Msg
hint userInput =
  case userInput of
    Initial -> div [] []
    Invalid _ error -> div [class "invalid"] [text error]
    Valid _ _ -> div [class "valid" [ text "Valid!"]

```

Is this any better? Well we’ve been able to flatten the conditional to some case statements. This approach also doesn’t grow exponentially as new cases and fields are added. In my opinion, this is a nicer way to write something views with validation.

## Finished product

I used the techniques described to build up a [full working example](https://ellie-app.com/3zzsjs5HtdQa1) on Ellie.

---

<div class="post-metadata">

**Author:** ![DylanLester](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dylanlester/32/1542_2.png) [@DylanLester](https://discourse.elm-lang.org/u/DylanLester)\
**Post date:** [October 19, 2018, 12:58pm UTC](https://discourse.elm-lang.org/t/handling-nested-conditional-logic/2163/6 "2018-10-19T12:58:58Z")

</div>

Wow, thanks a lot guys. Some really interesting ideas and great help.

I am somewhat familiar with Maybes but it really hadn’t occurred to me that you can just pass around an (error, value) tuple. That’s really quite an interesting solution. It sounds like it’s In the vein of passing around a potentially valid Promise in JavaScript.

Also, it’s extremely interesting building a model in such a way to avoid undesired states. The “Making impossible states impossible” lecture does explain this quite well but it’s going to require a re-watch.  
Thanks a lot for the detailed example @joelq. I was trying to create a similar model to that of your `Validated` type. Mine looked something like this:

```elm
type Input
    = Valid String
    | Invalid String
    | Unknown String

```

It looks like I was headed in the right direction but I was having trouble getting my head around custom types, tagged types and achieving flat case statements, so your examples are a great help.

Thanks guys for the help. It’s really great to get insight into a functional way of doing things and switching up my procedural way of thinking. Your guidance is much appreciated.

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [October 29, 2018, 12:58pm UTC](https://discourse.elm-lang.org/t/handling-nested-conditional-logic/2163/7 "2018-10-29T12:58:59Z")

</div>

Closed on October 29, 2018, 12:58pm UTC

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
