-
Notifications
You must be signed in to change notification settings - Fork 8
(#40) An attempt at gently explaining curried functions. #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much for this! I have some thoughts, but overall I love the new framing of what a function is and the build up to partial application.
|
|
||
| `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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love the changes and expansion on this explanation of what a function is in Gren. Some feedback on this paragraph in particular:
Very importantly and very different from most other programming lanagues in Gren, a function will always return the same result...
I don't like to assume people are always coming from mainstream languages when teaching Gren and don't want to repeatedly frame things in terms of how it's different from "most languages". I don't think the comparison is needed anyway and it's enough to say "Very importantly, in Gren, a function will always...". Especially since you touch on how it's obvious for this function but maybe not for other types of functions.
We'll get into
Commandand how Gren accomplishes this in a later section
Might be too soon to mention Command. What if you just say "We'll get into how Gren accomplishes this in a later section"?
| ``` | ||
|
|
||
| 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
letkeyword allows this and is discussed in a later section
Typo on that first word (should be "Or"?) but I think this would read better if the "Or" was removed and the sentences were condensed to:
If you want to write a more complex function in a step-by-step way, the
letkeyword allows for this and is covered in a later section.
| `|>` 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We haven't covered types at this point in the book so I think this section is premature. Perhaps it could be worked into the Types page? Function type signatures are introduced there.
|
|
||
| ## 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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
helping you to isolate type checking problems
I don't want to assume people are even familiar with the concept of "type checking" yet. What if this said "helping the compiler give you better error messages" or something along those lines?
| 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! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love this explanation of partial application. I think the second half gets a little confusing though. Starting here:
First the argument
60is applied, then the argument24...
My suggestion:
First the argument
60is applied, then the argument24is applied to the result of that. But what is the result ofmultiply 60? You might think it's an error, but in Gren it is not!
|
|
||
| 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` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you start with multiply 60 in the text above, maybe better to use 60 instead of 24 in this and the following examples.
|
|
||
| `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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to give this some thought but my initial reaction was that framing this in terms of type signatures and lining up the arguments is confusing. I have a video where I tried to explain the concept here. If you want to take a look maybe we can come up with the best way to explain this together.
Here is an attempt at explaining curried functions in a way that builds up to the concept gradually.