Functions are one of the core building blocks of Elm. In this lesson we learn how to define, call, pass parameters, receive parameters, partially apply functions, compose functions, and add correct type signatures. In this lesson we will show how to define functions with type signatures.
I'm going to add a new function to this file by typing the function name, "Ask," and then the first parameter name, "Thing." For now, we'll just have one parameter. I'll throw in an equal sign, and everything to the right of the equal sign will be the body of the function.
Let's do some string concatenation here. We're going to type, "Is there a..." leave a trailing space, then we'll use the ++, which is the string concatenation operator in Elm. Then we'll drop in, "Thing" right there, add another ++, and then the question mark.
Now we've got a function that should take a string and return another string. Let's use it in our main function. Here, I'll insert the lesser than character and then the pipe character. These two characters combined make what's called the pipe operator.
What this means is that everything on the right of the pipe operator will be evaluated and then passed in to whatever is on the left of the operator. In this case, let's type, "Ask" and then, "Fish." Save the file, go to the browser and reload. Is there a fish? It worked.
Elm is a strictly typed language, but you'll notice that we don't have any type definitions here at the moment. That's because it does some pretty impressive type inference. However, it is good practice to insert type definitions on their own, so let's do that.
Ask, and then add colon. That indicates to Elm where about to start a type definition. Then we'll put in a type. In this case, string the type of thing. Then we also need to add the return type. We can do that by putting in the minus character, and then the greater than character.
Now, we can insert the type of our return value, string. This is the function from string to string. In Elm, the type definitions are parameters separated by arrows, and then whatever is after the last arrow is the type of the return value.
What if we want to add another argument to this function? Let's go down and type in place. Then we'll insert another string here, "Is there a thing in the+place ++?" This is getting a little long, so I'll go back and make a line break wherever there are concatenation operators.
Let's save the file and reload in the browser again. Oops, looks like we got an error. It's saying, "The type annotation for ask doesn't match its definition." Our annotation says that we're taking a string and returning a string but, in reality, we seem to be taking two strings and returning a string.
That's the fact. We forgot to modify our type definition here. At the top, let's add in another string and then an arrow, save the file, try it in the browser. Looks like we've got another error. It says, "The right argument of the pipe is causing a type mismatch."
We've got ask fish, but the pipe is expecting the right argument to be a string, and instead we've got a function from string to string. Let's look at that again. Down here, we're calling ask with one argument, but it takes two. Elm's type system is telling us that what it's got is a function from string to string instead of the value string.
This is because in Elm, all functions are carried by default. Practically, carry just means that I can call a function with fewer parameters than it requires, and what I'll get back is a function that just requires the remaining parameters.
In this case, ask fish, instead of returning a string, returns a function that takes a string and then returns the string. Let's fix that down here by adding in our next value, sock. It works this time. Is there a fish in the sock? I don't know, you tell me.
Let's try working with more than one function. Let's make a new one above here. We're going to call it, "Politely." It's going to take a string and return a string. Here's our definition, politely, phrase, equals excuse me, and then we'll add the phrase.
Let's take the result of calling ask and pass that into politely, and then pass that into me. Down here, we can do that pretty easily by adding another pipe operator. For the first pipe operator, I'll add a return, and right above, on the next line here, I'll add another pipe operator, and I'll type in "politely."
What that's going to do is take the very last one and evaluate it first. This returns the string. Then it will get passed into politely, which takes a string. That will return a string, which gets passed into text. Let's try it out on the browser. It worked.
Here is where we're going to get into some of the really powerful concepts, the function composition in Elm. Let's make another new function. We're going to call it, "Ask politely about fish." It's going to take a string, and it's going to return a string. Here's the definition. You're going to note something different this time.
I'm not going to specify any parameters. That's because we're going to use what's called a point-free style. I'll enter my first function name here, politely, and then I'm going to use another character that's built into Elm, which is the double less than character.
This character is for function composition. This is built for taking two functions that have compatible inputs and outputs, and building them up to make one function. On the right side of this operator, I'll put parens, and then I'll type, "Ask fish."
What I've got inside these parens is a partially applied function. In other words, I have called ask with the first argument, and I've gotten back a function that takes the second argument and then gives us the return value.
What this needs is a place. I've put that together, so now I've got a function that takes a string and returns a string. Then I'm telling Elm that I want to combine this with another function that takes a string a string and returns a string. Then I want to assign that whole value to ask politely about fish.
Ask politely about fish on the right side will take the place, run it through ask fish, and then it will run it through politely, and then we're going to get a return value of a string. Down here, instead of doing these two calls, I can go ahead and call ask politely about fish, hat. Save the file, go to the browser, reload. It worked.
What we've done here is define a function that takes a couple of arguments. We've talked about type annotations a bit. We've talked about function composition. We've talked about the composition operator, a point free style.
We've talked about the pipeline operator. We've talked about partial application, and we've put together a nice function that can be reused with any value like hat to ask where the fish is.