From faa5cd457d312a85f298e901cce16ea70c25e393 Mon Sep 17 00:00:00 2001 From: David Koontz Date: Mon, 24 Mar 2025 23:41:57 -0700 Subject: [PATCH 1/2] (#40) An attempt at gently explaining curried functions. --- src/content/docs/syntax/functions.md | 99 ++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/src/content/docs/syntax/functions.md b/src/content/docs/syntax/functions.md index 2d12366..a757277 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 and _very different_ from most other programming lanagues 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 `Command` and 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. Of if you want to make more complex functions it is very handy to be able to carry out the work in a step-by-step way. The `let` keyword allows this and is discussed 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,83 @@ 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)`. +## Function type signatures + +Gren is really great at inferring the types for your functions but it's often helpful to put an explicit type declaration on your function. This can serve as documentation as well as helping you to isolate type checking problems. Adding a type signature to the multiply function from above would look like this: + +```elm +multiply : Int -> Int -> Int +multiply first second = + first * second +``` + +What this type says is "multiply is a function that takes an Int and an Int as input, and returns an Int". You may notice that the symbol between the two arguments is the same as the symbol before the return type. This is not a coincidence. What's happening here is that functions in Gren always 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`. So what would happen if we only provided one of the arguments? If you're used to how other programming languages work you might expect this to be an error, but in Gren it is not! + +`oneArgument = multiply 24` + +So what is the type of `oneArgument`? It's not `Int` since that would be the result of providing both arguments. If we line up the one argument with the type signature it gives us a clue. + +```elm +multiply: Int -> Int -> Int +multiply 24 +``` + +what's left is the remaining part of the type signature, `Int -> Int` meaning that `oneArgument` has the type `Int -> Int`. This is called partial application and allows us to build up all the arguments for a function one at a time. This is what was going on in the function pipeline example before. + +```elm +secondsInYear = + 60 -- Int + |> multiply 60 -- Int -> Int + |> multiply 24 -- Int -> Int + |> multiply 365 -- Int -> Int +``` + +`multiply` is a function of type `Int -> Int -> Int` but `multiply 60` partially applies it, leaving us with just `Int -> Int`. The `|>` operator provides `60` as the second argument so now we have a fully applied function and we get the resulting `Int` which gets passed along by the next `|>` to `multiply 24` which is also of type `Int -> Int` and so on. + +What's really going on under the hood is that `multiply` is actually a function that has this type: `multiply: Int -> (Int -> Int)` meaning "multiply is a function that takes an `Int` and returns a function of type `Int -> Int`. So when we say `multiply 24 60` we're first applying the 24, getting back the resulting function which is `Int -> Int`, then applying the 60 to that function to get the final Int. + +```elm +multiply : Int -> (Int -> Int) +multiply 24 + +result : Int -> Int + 60 + +result : Int + 1440 +``` + +Functions that take one argument at a time like this are known as `curried` functions. In some languages you can make functions be curried but you have to do it yourself, in Gren it's automatic. + +The one place that curried functions might cause you trouble is when you don't give a function the correct number of arguments by mistake. Let's say you're using a function like this + +```elm +validateUser : User -> Email -> DateTime -> Flags -> Status +``` +That's a lot of arguments and you might easily forget one of them. + +```elm +validateUser : User -> Email -> DateTime -> Flags -> Status +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 very descriptive so you can more easily work out what has gone wrong. + + + ## Anonymous Functions You can create functions without giving them a name: From 59de1217d7611cfa1ee0a7aa2ed805de1b3ea059 Mon Sep 17 00:00:00 2001 From: David Koontz Date: Sun, 1 Feb 2026 05:42:57 -0700 Subject: [PATCH 2/2] Address PR comments --- src/content/docs/syntax/functions.md | 78 +++++----------------------- src/content/docs/syntax/types.md | 47 +++++++++++++++++ 2 files changed, 61 insertions(+), 64 deletions(-) diff --git a/src/content/docs/syntax/functions.md b/src/content/docs/syntax/functions.md index a757277..9b31ccd 100644 --- a/src/content/docs/syntax/functions.md +++ b/src/content/docs/syntax/functions.md @@ -12,7 +12,7 @@ addOne number = `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 and _very different_ from most other programming lanagues 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 `Command` and 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. +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. @@ -24,7 +24,7 @@ sumOfFiveAndTwo = sumOf 5 2 ``` -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. Of if you want to make more complex functions it is very handy to be able to carry out the work in a step-by-step way. The `let` keyword allows this and is discussed in a later section. +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 return exactly one value. @@ -65,82 +65,32 @@ 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)`. -## Function type signatures +## Partial Application -Gren is really great at inferring the types for your functions but it's often helpful to put an explicit type declaration on your function. This can serve as documentation as well as helping you to isolate type checking problems. Adding a type signature to the multiply function from above would look like this: +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 -multiply : Int -> Int -> Int -multiply first second = - first * second +multiplyBySixty = multiply 60 ``` -What this type says is "multiply is a function that takes an Int and an Int as input, and returns an Int". You may notice that the symbol between the two arguments is the same as the symbol before the return type. This is not a coincidence. What's happening here is that functions in Gren always 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`. So what would happen if we only provided one of the arguments? If you're used to how other programming languages work you might expect this to be an error, but in Gren it is not! - -`oneArgument = multiply 24` - -So what is the type of `oneArgument`? It's not `Int` since that would be the result of providing both arguments. If we line up the one argument with the type signature it gives us a clue. +`multiplyBySixty` is now a new function that takes one argument and multiplies it by 60. We can use it like any other function: ```elm -multiply: Int -> Int -> Int -multiply 24 +multiplyBySixty 24 +-- returns 1440 ``` -what's left is the remaining part of the type signature, `Int -> Int` meaning that `oneArgument` has the type `Int -> Int`. This is called partial application and allows us to build up all the arguments for a function one at a time. This is what was going on in the function pipeline example before. +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 -- Int - |> multiply 60 -- Int -> Int - |> multiply 24 -- Int -> Int - |> multiply 365 -- Int -> Int -``` - -`multiply` is a function of type `Int -> Int -> Int` but `multiply 60` partially applies it, leaving us with just `Int -> Int`. The `|>` operator provides `60` as the second argument so now we have a fully applied function and we get the resulting `Int` which gets passed along by the next `|>` to `multiply 24` which is also of type `Int -> Int` and so on. - -What's really going on under the hood is that `multiply` is actually a function that has this type: `multiply: Int -> (Int -> Int)` meaning "multiply is a function that takes an `Int` and returns a function of type `Int -> Int`. So when we say `multiply 24 60` we're first applying the 24, getting back the resulting function which is `Int -> Int`, then applying the 60 to that function to get the final Int. - -```elm -multiply : Int -> (Int -> Int) -multiply 24 - -result : Int -> Int - 60 - -result : Int - 1440 -``` - -Functions that take one argument at a time like this are known as `curried` functions. In some languages you can make functions be curried but you have to do it yourself, in Gren it's automatic. - -The one place that curried functions might cause you trouble is when you don't give a function the correct number of arguments by mistake. Let's say you're using a function like this - -```elm -validateUser : User -> Email -> DateTime -> Flags -> Status + 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 ``` -That's a lot of arguments and you might easily forget one of them. - -```elm -validateUser : User -> Email -> DateTime -> Flags -> Status -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 very descriptive so you can more easily work out what has gone wrong. - +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 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