diff --git a/src/content/docs/syntax/functions.md b/src/content/docs/syntax/functions.md index 2d12366..9b31ccd 100644 --- a/src/content/docs/syntax/functions.md +++ b/src/content/docs/syntax/functions.md @@ -3,7 +3,18 @@ title: Functions description: Functions in Gren --- -A function returns different values depending on its input. In Gren, a function will always return the same result if provided the same input. +If constants allow you to give a name to a value, a function allows you to give a name to a calculation that produces a value. Functions take one or more inputs and return a single value. + +```elm +addOne number = + number + 1 +``` + +`addOne` is the name of the function and it is defined as being `number + 1`. If you do something like `result = addOne 7` then `result` will equal 8. + +Very importantly, in Gren, a function will always return the same result if provided the same input. This might seem obvious for something like `addOne` but it's also true of things such as updating a complex bit of state in an application or even making a HTTP request. We'll get into how Gren accomplishes this in a later section, for now it's enough just to know that Gren makes the guarantee that given the same input to a function, you will always get the same result. + +Ok now let's see what a function that has more than one argument looks like. ```elm sumOf first second = @@ -13,9 +24,9 @@ sumOfFiveAndTwo = sumOf 5 2 ``` -Here, `sumOf` is the name of the function, while `first` and `second` are the inputs to the function. The return value of `sumOf` is the last computed expression. In this case, the only expression is `first + second`, the result of which becomes the returned value. +Here, `sumOf` is the name of the function, while `first` and `second` are the inputs to the function. If you are familiar with another programming language you may have noticed the absence of any sort of `return` statement. That's because in Gren everything is an expression. The body of a function is just a single expression too. In this case, the expression is `first + second`, the result of which becomes the returned value. If you want to write a more complex function in a step-by-step way, the [`let`](/book/syntax/lets/) keyword allows for this and is covered in a later section. -Functions can take an arbitrary number of arguments, but must return exactly one value. +Functions can take an arbitrary number of arguments, but return exactly one value. Function arguments can be passed on multiple lines: @@ -30,7 +41,7 @@ It looks kind of silly here, but it is helpful to know this when you’re readin ## Function Pipelines -You can chain multiple function calls with parenthesis: +When you need the result of one function as an input to another, you can use parenthesis to indicate this: ```elm multiply first second = @@ -39,8 +50,9 @@ multiply first second = secondsInYear = multiply 365 (multiply 24 (multiply 60 60)) ``` +Here the result of `multiply 60 60` becomes the second argument to `multiply 24` and the result of THAT becomes the second argument to `multiply 365`. This "inside-out" nesting is common in other programming languages and it works the same here, but Gren has a way of making this kind of chaining really nice to read. -But you can make this more readable with the `|>` operator: +We can make the above expression more readable with the `|>` operator: ```elm secondsInYear = @@ -53,6 +65,33 @@ secondsInYear = `|>` passes the value on the left as the last argument to the function on the right. For example `sumOf 1 2 |> multiply 3` is the same as `multiply 3 (sumOf 1 2)`. +## Partial Application + +Functions in Gren take their arguments one at a time. When we do `multiply 60 24` we're actually doing this `((multiply 60) 24)`. First the argument `60` is applied, then the argument `24` is applied to the result of _that_. But what is the result of `multiply 60`? You might think it's an error, but in Gren it is not! + +```elm +multiplyBySixty = multiply 60 +``` + +`multiplyBySixty` is now a new function that takes one argument and multiplies it by 60. We can use it like any other function: + +```elm +multiplyBySixty 24 +-- returns 1440 +``` + +This is called partial application and it's what makes the pipeline operator work. In the pipeline example above, `multiply 60` returns a new function waiting for one more argument, and `|>` provides that argument. + +```elm +secondsInYear = + 60 + |> multiply 60 -- multiply 60 returns a function, 60 is passed to it + |> multiply 24 -- multiply 24 returns a function, result is passed to it + |> multiply 365 +``` + +Functions that take one argument at a time like this are known as curried functions. In some languages you have to do this yourself, in Gren it's automatic. + ## Anonymous Functions You can create functions without giving them a name: diff --git a/src/content/docs/syntax/types.md b/src/content/docs/syntax/types.md index eaacf4c..3fe237a 100644 --- a/src/content/docs/syntax/types.md +++ b/src/content/docs/syntax/types.md @@ -32,6 +32,53 @@ For functions it get's a little bit more complicated. `->` can be read as `to`. When reading the type signature of a function, the last `->` points to the return value of the function, while the types before represent the inputs. +While Gren is good at inferring the types for your functions, it's often helpful to put an explicit type declaration on your function. This can serve as documentation as well as helping the compiler give you better error messages. + +## Function Types and Partial Application + +You may notice that the `->` symbol between function arguments is the same as the symbol before the return type. This is not a coincidence. As covered in the [functions](/book/syntax/functions/#partial-application) section, functions in Gren take their arguments one at a time, which is called curried form. + +Type signatures reveal what's really going on. A function like `multiply : Int -> Int -> Int` is actually `multiply : Int -> (Int -> Int)` - a function that takes an `Int` and returns a function of type `Int -> Int`. The first argument has been applied, but not the second so you have a partially applied function. + +```elm +multiply : Int -> (Int -> Int) +multiply 60 + +result : Int -> Int + 24 + +result : Int + 1440 +``` + +One place where this might cause confusion is when you accidentally forget an argument. Consider a function like this: + +```elm +validateUser : User -> Email -> DateTime -> Flags -> Status +``` + +With that many arguments, you might easily forget one: + +```elm +userStatus = validateUser user currentEmail now +``` + +`userStatus` is not a value of type `Status` - it is a function of type `Flags -> Status`. It can be confusing when you go to compare `userStatus` with a `Status` like this `if userStatus == LoggedIn then` and get an error: + +``` +TYPE MISMATCH - I need both sides of (==) to be the same type: + +1| if userStatus == LoggedIn then + #^^^^^^^^^^^^^^^^^^^^# +The left side of (==) is: + #Flags -> Status# + +But the right side is: + Status +``` + +Fortunately Gren's error messages are descriptive so you can work out what has gone wrong. + ## Type Aliases ```elm