Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 24 additions & 24 deletions text/chapter3.md
Original file line number Diff line number Diff line change
Expand Up @@ -481,13 +481,13 @@ In the case of `insertEntry`, _eta conversion_ has resulted in a very clear defi

## Property Accessors

One common pattern is to use a function to access an individual fields (or "properties") of a record. An inline function to extract an `Address` from an `Entry` could be written as:
One common pattern is to use a function to access individual fields (or "properties") of a record. An inline function to extract an `Address` from an `Entry` could be written as:

```haskell
\entry -> entry.address
```

PureScript provides an equivalent [_property accessor_](https://github.com/purescript/documentation/blob/master/language/Syntax.md#property-accessors) shorthand, where an underscore is followed by a field name, so the inline function above is equivalent to:
PureScript also allows [_property accessor_](https://github.com/purescript/documentation/blob/master/language/Syntax.md#property-accessors) shorthand, where an underscore acts as the anonymous fuction argument, so the inline function above is equivalent to:
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inner thesaurus wants just one "equivalent" in this sentence.


```haskell
_.address
Expand Down Expand Up @@ -561,7 +561,7 @@ This type signature says that `findEntry` takes two strings, the first and last
And here is the definition of `findEntry`:

```haskell
findEntry firstName lastName book = head $ filter filterEntry book
findEntry firstName lastName book = head (filter filterEntry book)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there's a bunch of content to get through before the explanation of $, I figured it would be better to reveal the alternative version later on.

where
filterEntry :: Entry -> Boolean
filterEntry entry = entry.firstName == firstName && entry.lastName == lastName
Expand Down Expand Up @@ -591,7 +591,7 @@ However, this chapter has also included examples of _infix_ [binary operators](h
infix 4 eq as ==
```

and therefore `entry.firstName == firstName` in `filterEntry` could be replaced with the `eq entry.firstName firstName`.
and therefore `entry.firstName == firstName` in `filterEntry` could be replaced with the `eq entry.firstName firstName`. We'll cover a few more examples of defining infix operators later in this section.
Comment on lines 591 to +594
Copy link
Author

@milesfrain milesfrain Jan 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine some readers getting hung-up on not fully understanding that infix code.


There are situations where putting a prefix function in an infix position as an operator leads to more readable code. One example is the `mod` function:

Expand Down Expand Up @@ -621,7 +621,7 @@ book3 = insertEntry john (insertEntry peggy (insertEntry ned emptyBook))
book4 = john `insertEntry` (peggy `insertEntry` (ned `insertEntry` emptyBook))
```

We can also define an operator alias/synonym for `insertEntry.` We'll arbitrarily choose `++` for this operator, give it a [precedence](https://github.com/purescript/documentation/blob/master/language/Syntax.md#precedence) of `5`, and make it right [associative](https://github.com/purescript/documentation/blob/master/language/Syntax.md#associativity) using `infixr`:
We can also define an infix operator alias (or synonym) for `insertEntry.` We'll arbitrarily choose `++` for this operator, give it a [precedence](https://github.com/purescript/documentation/blob/master/language/Syntax.md#precedence) of `5`, and make it right [associative](https://github.com/purescript/documentation/blob/master/language/Syntax.md#associativity) using `infixr`:

```haskell
infixr 5 insertEntry as ++
Expand All @@ -630,7 +630,7 @@ infixr 5 insertEntry as ++
This new operator lets us rewrite the above `book4` example as:

```haskell
book6 = john ++ (peggy ++ (ned ++ emptyBook))
book5 = john ++ (peggy ++ (ned ++ emptyBook))
```

and the right associativity of our new `++` operator lets us get rid of the parentheses without changing the meaning:
Expand All @@ -639,11 +639,16 @@ and the right associativity of our new `++` operator lets us get rid of the pare
book6 = john ++ peggy ++ ned ++ emptyBook
```

Likewise, in the code for `findEntry` above, we used a different form of function application: the `head` function was applied to the expression `filter filterEntry book` by using the infix `$` symbol.
Another common technique for eliminating parens is to use `apply`'s infix operator `$`, along with your standard prefix functions.

For example, the earlier `book3` example could be rewritten as:
```haskell
book7 = insertEntry john $ insertEntry peggy $ insertEntry ned emptyBook
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relocated this example closer to the previous snippet so we can avoid the recap.

Also a fan of showing the most compelling example first, then the deeper explanation. So I did some other reordering. I think many beginning users can just make a mental note of what $ means in practice, and be productive with it without necessarily being able to write the code for apply if quizzed.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is much tidier.

```

This is equivalent to the usual application `head (filter filterEntry book)`
Substituting `$` for parens is usually easier to type and (arguably) easier to read. A mnemonic to remember the meaning of this symbol is to think of the dollar sign as being drawn from two parens that are also being crossed-out, suggesting the parens are now unnecessary.

`($)` is just an alias for a regular function called `apply`, which is defined in the Prelude. It is defined as follows:
Note that `$` isn't special syntax that's hardcoded into the language. It's simply the infix operator for a regular function called `apply`, which is defined in the Prelude as follows:

```haskell
apply :: forall a b. (a -> b) -> a -> b
Expand All @@ -652,23 +657,15 @@ apply f x = f x
infixr 0 apply as $
```

So `apply` takes a function and a value and applies the function to the value. The `infixr` keyword is used to define `($)` as an alias for `apply`.

But why would we want to use `$` instead of regular function application? The reason is that `$` is a right-associative (`infixr`), low precedence (`0`) operator. This means that `$` allows us to remove sets of parentheses for deeply-nested applications.

For example, the above nested function application to create an `AddressBook` with three entries:

```haskell
book3 = insertEntry john (insertEntry peggy (insertEntry ned emptyBook))
```

becomes (arguably) easier to read when expressed using `$`:
The `apply` function takes another function (of type `(a -> b)`) as its first argument and a value (of type `a`) as its second argument, then calls that function with that value. If it seems like this function doesn't contribute anything meaningful, you are absolutely correct! Your program is logically identical without it (see [referential transparency](https://en.wikipedia.org/wiki/Referential_transparency)). The syntactic utility of this function comes from the special properties assigned to its infix operator. `$` is a right-associative (`infixr`), low precedence (`0`) operator, which lets us remove sets of parentheses for deeply-nested applications.

Another parens-busting opportunity for the `$` operator is in our earlier `findEntry` function:
```haskell
book6 = insertEntry john $ insertEntry peggy $ insertEntry ned emptyBook
findEntry firstName lastName book = head $ filter filterEntry book
```
We'll see an even more elegant way to rewrite this line with "function composition" in the next section.

Wrapping an infix operator in parentheses lets you use it as a prefix function:
If you'd like to use a concise infix operator alias as a prefix function, you can surround it in parentheses:

```text
> 8 + 3
Expand All @@ -678,22 +675,25 @@ Wrapping an infix operator in parentheses lets you use it as a prefix function:
11
```

Alternatively, operators can be partially applied by surrounding them with parentheses and using `_` as an operand in an [operator section](https://github.com/purescript/documentation/blob/master/language/Syntax.md#operator-sections):
Alternatively, operators can be partially applied by surrounding the expression with parentheses and using `_` as an operand in an [operator section](https://github.com/purescript/documentation/blob/master/language/Syntax.md#operator-sections). You can think of this as a more convenient way to create simple anonymous functions (although in the below example, we're then binding that anonymous function to a name, so it's not so anonymous anymore):

```text
> add3 = (3 + _)
> add3 2
5
```

To summarize, the following are equivalent definitions of a function that adds `5` to its argument:
To summarize, the following are equivalent definitions of a function that adds `5` to its argument:

```haskell
add5 x = 5 + x
add5 x = add 5 x
add5 x = (+) 5 x
add5 x = 5 `add` x
add5 = add 5
add5 = \x -> 5 + x
add5 = (5 + _)
add5 x = 5 `(+)` x -- Yo Dawg, I herd you like infix, so we put infix in your infix!
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too much?

Was hoping that this would work too:

add5 x = (`add`) 5 x

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My feeling is that book's overall tone is a bit drier, so I don't know how well the comment on the last line fits.

Personally, I like it, and earlier I had considered changing your "john" "peggy" "ned" example to "homer" "apu" "ned", but decided against it, for consistency.

```

## Function Composition
Expand Down