The Hunted

Dalir as he was known on the streets of Tehran was growing hungry. Over the last few weeks, he had been eating on a regular schedule and realized he was getting accustomed to it. He had no idea of…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Refactoring an http Request Into a State Machine in Elm

I gave a talk on elm at a local front end meetup. I had 45 minutes. Teach them elm? There’s plenty of youtube videos for that. I wanted to inspire them. Hoping they would try elm in their spare time. I had to make a compelling pitch. I had to show them how different it could be to write in elm. So I started thinking of what was different between what I write in react / redux from what I write in elm. And union types came to mind. But how? I needed something that most everyone in the crowd would be familiar with. Http requests. If you’ve spent any time in front end development chances are you’ve made plenty of them. And so we walked through refactoring an http request into a state machine. Below is how we did it.

This article breaks down each branch so you can see the changes to the model, update and view as you read through it.

code not relevant to the example is replaced with …

This is the first branch, and how I began writing code in elm coming from react / redux.

model and init

Our model has 2 boolean flags (isLoading and resultsReceived), a list of repos (empty to start), and an error message (maybe string).

So why resultsReceived? Because it’s a necessity for good ux as we have to differentiate between an empty list returned from our request and our initial empty list in our model. This way we show nothing when our app starts up and a message saying our request returned no results if a request returns no repos.

our update function

The booleans we’ve set up in our model need to be set and unset in our update function. These are easy to forget about, and the only way to be sure you haven’t forgotten to set them correctly is through testing, or user trial and error.

The errorMessage is set to a Maybe String, so we have help from the compiler when working with it in our update function. That’s nice, but should it be separate from the repos returned from our http request? (we’re getting ahead of ourselves)

There’s a lot to remember here and little help from the compiler. This means we’re going to need a lot of checks in our view function.

What do we need to track and display in our view? The following. Be prepared, this is a mess.

And my mind is now swimming. These if statements are just nasty. Be warned, there’s some ugly code coming up.

our view function.

Let’s recap…this is terrible!

These if statements are giving me a headache. They’re a result of the booleans we need to check for. Let’s start our refactor by getting rid of one of them.

We should start our refactor at the bottom of our view function. Those two if checks are there because we need to check if results from our http request have been received. Meaning, we need to differentiate between our initial empty list and one that returns no repos from a request. How can we better describe the empty list of repos? How about with a Maybe type? That just might work. (haha)

Instead of checking if our list is empty, or if we have made a request, we can set the initial state of repos in our model to Nothing. And Nothing represents repos much better at this point as we really don’t have anything. We haven’t made a request yet, so how can we have any data? We established that an empty list back from an http request is data, it’s just data that’s telling us we got zero repos back. That is different than not making a request at all and having nothing. So let’s do that. let’s set repos to be a Maybe (List Repo) and see how that changes our model, update and view.

model and init

One less boolean value to track. (no more requestReceived flag) That’s nice. It’ll make our update function easier to deal with. Also, our initial value for repos is now Nothing, which is more accurate than an empty list.

our update function.

Better, but still not great. At least we’re not tracking if we’ve made a request, which eliminated three boolean flags we had to set in our update function. Also we are now more descriptive with setting the value of repos to either Nothing or Just repos. (we’ll handle the Just portion of our repos in our view function)

our view function

That’s better. Now we only have to check if we are loading, or if there is an error message. The function repostTable (which takes the place of the last two if statements from our original version) can pattern match off of model.repos (Nothing or Just repos) to display the correct view. This is nice as we can leave it to the elm compiler to tell us if we have forgotten to take Nothing into account, and no longer have to rely on our brains (yikes) to check for the correct state.

Let’s recap. Better, but still not great. We’re now leveraging the elm compiler, which is a good thing, and our logic checks aren’t so daunting. But we’re still checking for loading and error messages. How can we get rid of these and still correctly display all our possible states?

This is it. We need to readjust our thinking. The http request does not rule us, we rule it, and in doing so we need to describe it so that our application can be written in a way that only allows us to display the states we declare it has. Enter…union types.

Our repos model is currently described as a Maybe (List Repo) but it’s more than that. It’s really an http request waiting to hold the data we get back from it. So how would we describe that? Well, we’ve established that we need to differentiate between a requested and not requested state, so let’s start there and step through an http request.

No other values will be possible for model.repos and the elm compiler will help us enforce that.

Now, if only elm gave us the ability to set a type and give it values. Something similar to the Bool type that has True and False as its values. Union Types to the rescue! Let’s name our type ExternalRequest capable of taking error and data and see how that changes our app.

our model. Pay particular attention to the type ExternalResource error data, and the values it has.

Wow! We’ve eliminated isLoading and errorMessage from our model. Nice. And we’ve got a new union type called ExternalResource with the values we’ve declared for it. We can now set repos to have a value of ExternalResource Http.error (List Repo), which is a type we created and have set the values for. These can now be applied to our repos model in our init and update function.

Our init is now much smaller and sets repos to the value of NotRequested.

We can follow the same logic in our update function. All we have to do is set repos to the correct value for each message and move along. If we’re fetching repos, then repos is equal to Loading, when we receive our results, we set the Ok pattern match of result to Success repos, and the Err case to Failure error. This is far easier than having to remember to set and unset flags all over the place.

our update function

Much easier to read. It’s also clear what the value of repos is throughout our update function.

And for the grand finale, our view function

A simple pattern match off the values in model.repos and we display the appropriate view. No more boolean checks. And as we’re pattern matching off of a union type, the elm compiler will remind us to account for each value in our case statement. How does it do this? Because we set repos to have a type of ExternalResource with four specific values. The compiler knows the values we gave it, so it’ll yell at us (a nice yell) if we forget to account for any of them in our case statement.

And there we have it. A state machine. One that describes our http request, accounts for the possible states and displays them to the screen.

Add a comment

Related posts:

buy usa facebook accounts

Facebook is one of the oldest and most popular social media platforms. However, in this era, Facebook accounts are the prime assets of every person. The reason is now Facebook is not an entertaining…

Gratitude

Thank you to my legs. They work, and I appreciate that they work. I used them whenever I could. To walk. To run. To escape dark clouds with sweat and exhaustion and blood pumping and a clear head…

The Org Celebrates the Promotion of Derek Hanley to Director of Business Development

If you dont have a Derek on your team, get one! But you cant have ours. We are so excited to announce that Derek is elevated to Director of Business Development. “Could not have gotten to where I am…