Function basics
Functions are the bread and butter of a language like Elm. A function takes one or more arguments as input, and does some calculations with them to return a new value.
Believe it or not, you've already used some functions. When we talked about Strings
, we went over the toString
function. It takes any type of value as an argument and returns a String
.
> toString 42
"42"
> toString [ 1, 2, 3 ]
"[1,2,3]" : String
When we talked about Booleans, we went over the not
function. It takes a Bool
as an argument and returns its opposite.
> not True
False : Bool
> not (20 < 5)
True : Bool
Notice how we coerce the compiler into reading that expression 20 < 5
as a single argument by putting it in parentheses. In some languages, you need to use parentheses each time you call a function, but Elm only requires them in cases like this.
Reading functions' type signatures
Functions have type signatures just like any other value. You can get a function's type signature by entering it into the REPL with no arguments. Let's try this with not
.
> not
<function: not> : Bool -> Bool
That arrow tells us that not
takes a Bool
and returns a Bool
. We already knew that, but this is a quick and easy way to figure out how to use a function if you're not sure. A function's type signature tells you concisely the types of values it takes as arguments and returns.
Let's look at the type signature of toString
:
> toString
<function> : a -> String
As you can see, type variables can appear in a function's type signature, too. This a
is a concise way of telling us toString
can take a value of any type.
Getting functions from a module
To see some more exciting functions, you'll have to import a module. Let's import the String
module in the REPL so we can access some useful functions for working with String
s.
> import String
Now we can access all the functions in the String
module. One of these is String.toUpper
. Let's see what it does.
> String.toUpper
<function: toUpper> : String -> String
From the type signature of this function, we can tell it takes a String
as its argument and returns a new String
.
So if we try it out, there are no surprises:
> String.toUpper "Howdy!"
"HOWDY!" : String
You can import any of the modules in the Elm core package from the REPL.
Functions that take multiple arguments
Functions that take more than one argument aren't very different from single-argument functions. Just separate the arguments with spaces.
One easy-to-understand example is String.repeat. It takes two arguments: an Int
and a String
. Let's see what happens when we call this function.
> String.repeat 3 "ha"
"hahaha" : String
> String.repeat 5 "ho"
"hohohohoho" : String
As you can tell, it returns a new String
made by repeating the argument String
the same number of times as the Int
.
String.split
returns a List
of String
s. It also takes two String
s as arguments.
> String.split "-" "do-re-mi"
["do","re","mi"] : List String
> String.split "h" "ahohohooo"
["a", "o", "o", "ooo"] : List String
When a function takes multiple arguments, its type signature looks a little different. Every argument type is followed by an ->
arrow, and the return type comes last, as usual.
> String.repeat
<function: repeat> : Int -> String -> String
> String.split
<function: split> : String -> String -> List String