loading...

Elm: the jerk that took away my for-loops.

ryannhg profile image Ryan Haskell-Glatz ・4 min read

The good ole' days

The first time I ever coded was my junior year of high school.

The year was 2010, her name was Java.

System.out.println("Hello, Ryan!");

But let's pretend her name was Javascript:

console.log('Hello, Ryan!')

(Ah, less typing...)

Pretty soon, I found myself saying "hello" to all kinds of people.

console.log('Hello, Ryan!')
console.log('Hello, Scott!')
console.log('Hello, mom!')

Life was easy... until it wasn't.

Feeling stupid

My pal: "Ryan- what are you doing?"

Me: "I have no idea."

My pal: "You're supposed to use a for-loop for that."

Me: "wat."

My pal:

var names = [ 'Ryan', 'Scott', 'mom' ]

for (var i = 0; i < names.length; i++) {
  var name = names[i]
  console.log('Hello, ' + name + '!')
}

Me: "..."

Also me: "WAT."

My pal: "It's not too hard. Just make an array of strings, called "names". From there, we make a for-loop:

  1. Say var i = 0; to start the index at zero.
  2. We say i < names.length to tell the for loop when to stop looping.
  3. Then type i++ so it increments after every time it's called.

The variable i will be 0, 1, and 2. You can use it to index into your array to get the name, and then use that name variable so you don't have to duplicate the repetitive 'Hello' part."

Me: "wat."

Feeling un-stupid

The first time I met the for-loop, I had a lot of questions:

  1. "Indexing into an array?"
  2. "They start counting at 0?"
  3. "Why doesn't i++ get a semicolon?"

Copy-pasting console.log statements was easy! For loops introduced me to a lot of new concepts. After some practice, I became familiar with those concepts.

It wasn't so bad. In no time, I was using them everywhere!

Even better, I got to bring the for-loop with me everywhere I went: Java, C#, Javascript, C, C++.

For-loops were my life. And life was good!

...until I met Elm.

Feeling anti-stupid

Fast forward:

  • My name was still Ryan
  • I had a job in the city making websites.
  • I was a JS pro (or at least I could add two numbers)
function add (a, b) {
  return a + b
}

But on a train ride home, I saw an inspirational tech talk by a guy named Evan.

He designed something to make web programming nicer for folks!

The year was 2016, and her name was Elm.

add a b = a + b

(Ah, less typing)

The syntax was definitely spooky. It didn't look anything like JS!

But neither did HTML or CSS- and I learned those!

Just like for-loops, it took at bit of practice.

So I followed the official guide, asked questions in the #beginners Slack channel, and even started putting my commas on the left side.

[ "who"
, "have"
, "I"
, "become??"
]

Crazy, right?

Next thing you know, this HTML template:

<!-- view.html -->
<div class="app">
  <p>Hello, Ryan!</p>
  <p>Hello, Scott!</p>
  <p>Hello, mom!</p>
</div>

Became this Elm function:

view =
    div [ class "app" ]
        [ p [] [ text "Hello, Ryan!" ]
        , p [] [ text "Hello, Scott!" ]
        , p [] [ text "Hello, mom!" ]
        ]

Haha, what a breeze!

Feeling re-stupid

Wait a minute... look at all that duplication!

I know- I'll grab my trusty for-loop!

Let's hit up the Elm docs to find the function I'm looking for:

Module docs for the term "for", no results...

My god... Evan forgot to implement for-loops.

What a rookie mistake.

So I hopped on the Slack, to find out what to do.

A friendly Elm stranger let me know that List.map was the function I was looking for.

Me: "wat."

A friendly Elm stranger:

names =
    [ "Ryan", "Scott", "mom" ]

viewParagraph name =
    p [] [ text ("Hello, " ++ name ++ "!") ]

view =
    div [] (List.map viewParagraph names)

Me: "wat."

A friendly Elm stranger: "lol, noob"

(Just kidding, I've never been called a noob on the Elm slack)

A friendly Elm stranger: "Let's break it down a bit.

List.map is just a function, like view or viewParagraph.

It takes in two inputs:

  1. A function that turns "things" into "other things"

  2. A list of "things".

If you give it those inputs, it'll return back a list of "other things".

List.map : (things -> otherThings) -> List things -> List otherThings

In your example, you had a list of "strings" you wanted to turn into a list of "HTML".

So your "things" are "Strings", and your "other things" are "HTML"!

List.map : (String -> Html msg) -> List String -> List (Html msg)

Just tell List.map how to turn a "string" into "HTML elements" (viewParagraph), and give it your list of "strings" (names).

You'll get back your list of "HTML", and that's what you can print to the page."

Me: "..."

Also me: "I'm scared."

Feeling un-re-stupid

After I got some practice with List.map, I started getting the hang of it.

Soon afterwards, I found out JavaScript has the map function too!

And it's been there for a long time!

function sayHello (name) {
  console.log('Hello, ' + name + '!')
}

[ 'Ryan', 'Scott', 'mom' ].map(sayHello)

Elm forced me to use their weird loop thing.

I felt tricked! But then I liked it better.

Now I only use for-loops in blog posts.

So thanks, Evan.

I'm glad you forgot the for-loop.

Also your talk was pretty dope.

Thanks for reading! ❤️

Posted on by:

ryannhg profile

Ryan Haskell-Glatz

@ryannhg

Building a friendlier web 🌳

Discussion

pic
Editor guide
 

At the risk of earning myself a major woosh:

JS is a language of actions. It does things: "allocating" a variable and changing its value are the building blocks of a for loop.

Elm is a language of definitions. One list (of html elements) is a transformation of another list (in this case the function was constructed using map).

The compiler turns Elm's declarations into commands that can actually be executed.

So JS's map function is not the same as Elm's map function. It is just a more specific form of a for loop with some restrictions.

 

I disagree with this. JS is a multi-paradigm language where multiple disciplines can be used. JavaScript includes first-class functions, destructuring assignment, and other features which enable great support for functional style. You can avoid many object-oriented or imperative-style features of the language and skip all the headaches that come with them.

 

I totally agree with you!

The point of the post was not to say "JavaScript doesn't support functional programming"

It was just about my experience with a language that only supported functional things.

When I first got started with JS, I naturally used for loops because of my background with Java and imperative languages.

Elm forced me to try something different, and now that's the style I prefer using in JS too 😃

Thanks for your reply, sorry about causing the confusion!

 

I never said anything about functional style so I'm not sure what you're trying to disagree with here...

JS is a language of actions. It does things: "allocating" a variable and changing its value are the building blocks of a for loop.

I politely disagree with your summary of what JS is, that's all.

Oh sorry, that wasn't my quote 😅

Thought this was a direct reply to my article haha

"Functional style", ill-defined as it is, does not equate to not having effects / actions.

 

That's a really interesting perspective, thanks for sharing it!

I never thought about it that way 😀

 

If you want to understand more about it, Bartosz Milewski has a great series about the underlying theory. First two chapters should give you a pretty good idea of what's going on.

 

List.map isn't the only way to "loop" in Elm. Recursion is the construct for repeating something in a functional language. If map didn't exist, you could easily write it on your own using recursion -

map : (a -> b) -> List a -> List b
map f list =
    case list of
        [] -> []
        first :: more -> (f first) :: map f more

> map (\n -> n * n) [ 1, 2, 3, 4 ]
-- [ 1, 4, 9, 16 ] : number

> map (\n -> String.repeat n "x") [ 1, 2, 3, 4 ]
-- [ "x", "xx", "xxx", "xxxx" ] : List String

This goes for any program where a "loop" is needed. In some procedural language, you might write the classic fibonacci program -

function fibonacci (n = 0) {
  let a = 0, b = 1, temp
  for (let m = 0; m < n; m++) {
    temp = a
    a = b
    b = temp + b
  }
  return a
}

> fibonacci(10)
// 55

> fibonacci(50)
// 12586269025

But in a functional language like Elm, we use recursion and avoid side-effects like variable mutation and reassignment. The result is a program that is much easier to think about -

fibonacci : Int -> Int
fibonacci n =
    helper n 0 1

helper : Int -> Int -> Int -> Int
helper n a b =
    case n of
        0 -> a
        _ -> helper (n - 1) b (a + b)

> fibonacci 10
-- 55 : Int

> fibonacci 50
-- 12586269025 : Int