More on functions

If you've followed along from the beginning, you've actually already been introduced to a dozen or so functions. It's true--all those arithmetic operators, the concatenation operator, the cons operator, and the logical operators are actually functions! They're even documented just like any other function in the Elm package site. Along with not and toString, they can be found in a module called Basics, which is imported by default every time you open the REPL.

Infix functions

The only way those operators differ from regular functions is that they are used primarily in infix position. That is, rather than getting placed before their arguments, they go in between them. The fundamental principle is the same--you give them arguments, and they use those arguments to return a new value.

You can use any infix function just like a normal function if you surround it in parentheses.

> (+) 2 5
7 : number
> (/) 7 2
3.5 : Float
> (::) 1 [ 2, 3, 4 ]
[1,2,3,4] : List number
> (==) "foo" "oof"
False : Bool

Just like normal functions, you can enter these parentheses-enclosed operators into the REPL and get a type signature.

> (+)
<function> : number -> number -> number
> (/)
<function> : Float -> Float -> Float
> (::)
<function> : a -> List a -> List a
> (==)
<function> : a -> a -> Bool

Notice how type variables are used in these type signatures. In the type signature of (+), there is only one number type variable, with no number' or number''. This tells us some essential information--each number argument and the number being returned are the same type of number. So we know not to try adding an Int and Bool.

Likewise, the use of a in tell us all the most essential information about (::). This function takes a value of any type a, plus a List of values of that same type a. The List it returns will contain values all of type a, as well.

Using a normal function in infix position

Conversely, you can use any function that takes two arguments like an infix function. Just surround it in backticks.

> 3 `String.repeat` "ha"
"hahaha" : String
> "-" `String.split` "do-re-mi"
["do","re","mi"] : List String

You'll come across cases where using this infix syntax may make your code easier to read. For now, it's just something that's good to be aware of.

Writing your own functions

Like in most programming languages, you can easily give a value a name in Elm. You do this with =, not to be confused with the == equality operator.

> myNumber = 7
7 : number
> myNumber
7 : number
> myNumber * 2
14 : number
> myNumber == 7
True : Bool

When you know how to do that, you're just one step away from writing your own brand new function.

> double n = n * 2
<function> : number -> number

Here, before the = sign, we not only named our function double--we also gave our argument a name, n. (We could have named this however we wanted.) Now, when you call double with a number, like 7, it will return the value of the expression right after =, replacing n with 7.

> double 7
14 : number

You can reuse a function you write as many times as you want. It's just like any other function. As you'd expect, each time you call your function, the return value will be different depending on the arguments.

> double 2
4 : number
> double 10
20 : number

When you want your function to take multiple arguments, separate their names with spaces.

> multiply x y = x * y
<function> : number -> number -> number
> multiply 2 5
10 : number
> multiply 7 7
49 : number

As long as the expression on the right is valid Elm, you can transform your arguments however you want.

> shout message = (String.toUpper message) ++ "!"
<function> : String -> String
> shout "hello"
"HELLO!" : String
> shout "oops"
"OOPS!" : String

Curried functions

This particular concept isn't essential to a basic working knowledge of Elm, but it explains lots of the really cool behavior that makes the language stand out. So don't sweat it if this section isn't immediately clear.

Imagine you want a function that repeats a String three times. With your knowledge already know the String.repeat function, you could write something like this:

> repeatThree myString = String.repeat 3 myString
<function> : String -> String

But there's an easier way. Functions in Elm support partial application. That means you can call a function without all its arguments to create a new function. Here's an illustration.

> repeatThree = String.repeat 3
<function> : String -> String

String.repeat normally takes both an Int and a String. But when you call it with just one argument 3, you get a new function. Calling this new function is just like calling String.repeat with 3 as the first argument.

> repeatThree "blah"
"blahblahblah" : String
> repeatThree "hey "
> "hey hey hey " : String

This is why functions' type signatures look the way they do. Take another look at the type signature of String.repeat:

Int -> String -> String

If you add some parentheses, you get a visualization of what happens in partial application.

Int -> (String -> String)

Instead of a function taking two arguments, this now looks like a function that takes one argument and returns another function with the type signature String -> String.

You could think of all Elm functions as really only taking a single argument. When you call String.repeat 3 "ha", that's equivalent to this two-step process:

  1. Passing 3 to a function of type Int -> (String -> String)
  2. Passing "ha" immediately to the newly returned String -> String function.

results matching ""

    No results matching ""