Skip to content

Commit 6f498f2

Browse files
committed
Describe infix vs prefix based on AddressBook example.
1 parent f50148e commit 6f498f2

File tree

1 file changed

+69
-33
lines changed

1 file changed

+69
-33
lines changed

text/chapter3.md

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -579,18 +579,66 @@ Note that, just like for top-level declarations, it was not necessary to specify
579579

580580
## Infix Function Application
581581

582-
Most of the functions discussed so far used _prefix_ function application, where the function name was put _before_ the arguments. For example, when using the `findEntry` function to search an `AddressBook`, one might write:
582+
Most of the functions discussed so far used _prefix_ function application, where the function name was put _before_ the arguments. For example, when using the `insertEntry` function to add an `Entry` (`john`) to an empty `AddressBook`, we might write:
583583

584-
```text
585-
> findEntry "John" "Smith" addressBook
584+
```haskell
585+
> book1 = insertEntry john emptyBook
586586
```
587587

588-
However, this chapter has also included examples of _infix_ functions, such as the `==` function in the definition of `filterEntry`, where the function is put _between_ the arguments. These infix operators are actually defined in the PureScript source as infix aliases for their underlying implementations. For example, `==` is defined as an alias for the prefix `eq` function with the line:
588+
However, this chapter has also included examples of _infix_ [binary operators](https://github.com/purescript/documentation/blob/master/language/Syntax.md#binary-operators), such as the `==` operator in the definition of `filterEntry`, where the operator is put _between_ the two arguments. These infix operators are actually defined in the PureScript source as infix aliases for their underlying _prefix_ implementations. For example, `==` is defined as an infix alias for the prefix `eq` function with the line:
589589

590590
```haskell
591591
infix 4 eq as ==
592592
```
593593

594+
and therefore `entry.firstName == firstName` in `filterEntry` could be replaced with the `eq entry.firstName firstName`.
595+
596+
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:
597+
598+
```text
599+
> mod 8 3
600+
2
601+
```
602+
603+
This is fine, but doesn't line up with common usage (in conversation, one might say "eight mod three"). Wrapping a prefix function in backticks (\`) lets you use that it in infix position as an operator, e.g.,
604+
605+
```text
606+
> 8 `mod` 3
607+
2
608+
```
609+
610+
In the same way, wrapping `insertEntry` in backticks turns it into an infix operator, such that `book1` and `book2` below are equivalent:
611+
612+
```haskell
613+
book1 = insertEntry john emptyBook
614+
book2 = john `insertEntry` emptyBook
615+
```
616+
617+
We can make an `AddressBook` with multiple entries by using multiple applications of `insertEntry` as a prefix function (`book3`) or as an infix operator (`book4`) as shown below:
618+
619+
```haskell
620+
book3 = insertEntry john (insertEntry peggy (insertEntry ned emptyBook))
621+
book4 = john `insertEntry` (peggy `insertEntry` (ned `insertEntry` emptyBook))
622+
```
623+
624+
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`:
625+
626+
```haskell
627+
infixr 5 insertEntry as ++
628+
```
629+
630+
This new operator lets us rewrite the above `book4` example as:
631+
632+
```haskell
633+
book6 = john ++ (peggy ++ (ned ++ emptyBook))
634+
```
635+
636+
and the right associativity of our new `++` operator lets us get rid of the parentheses without changing the meaning:
637+
638+
```haskell
639+
book6 = john ++ peggy ++ ned ++ emptyBook
640+
```
641+
594642
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.
595643

596644
This is equivalent to the usual application `head (filter filterEntry book)`
@@ -606,60 +654,48 @@ infixr 0 apply as $
606654

607655
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`.
608656

609-
But why would we want to use `$` instead of regular function application? The reason is that `$` is a right-associative, low precedence operator. This means that `$` allows us to remove sets of parentheses for deeply-nested applications.
657+
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.
610658

611-
For example, the following nested function application, which finds the street in the address of an employee's boss:
659+
For example, the above nested function application to create an `AddressBook` with three entries:
612660

613661
```haskell
614-
_.street (_.address (_.boss employee))
662+
book3 = insertEntry john (insertEntry peggy (insertEntry ned emptyBook))
615663
```
616664

617665
becomes (arguably) easier to read when expressed using `$`:
618666

619667
```haskell
620-
_.street $ _.address $ _.boss employee
668+
book6 = insertEntry john $ insertEntry peggy $ insertEntry ned emptyBook
621669
```
622670

623-
Note that neither of the above examples is idiomatic PureScript. Real-world code is more likely to express this as:
624-
```haskell
625-
(boss employee).address.street
626-
```
627-
or
628-
```haskell
629-
_.boss.address.street employee
630-
```
671+
Wrapping an infix operator in parentheses lets you use it as a prefix function:
631672

632-
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:
633-
```text
634-
> mod 8 3
635-
2
636-
```
637-
This is fine, but doesn't line up with common usage. Wrapping a prefix function in backticks (\`) lets you use a prefix function in infix position as an operator, e.g.,
638-
```text
639-
> 8 `mod` 3
640-
2
641-
```
642-
Likewise, wrapping an operator in parentheses lets you use it as a function in prefix position:
643673
```text
644674
> 8 + 3
645675
11
646676
647677
> (+) 8 3
648678
11
649679
```
650-
This allows for compact definitions of curried (or partially applied) functions based on infix operator functions, such as the `add2` function below:
651-
```text
652-
> add2 = (+) 2
653-
> add2 4
654-
6
655-
```
680+
656681
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):
682+
657683
```text
658684
> add3 = (3 + _)
659685
> add3 2
660686
5
661687
```
662688

689+
To summarize, the following are equivalent definitions of a function that adds `5` to its argument:
690+
691+
```haskell
692+
add5 x = 5 + x
693+
add5 x = add 5 x
694+
add5 x = (+) 5 x
695+
add5 x = 5 `add` x
696+
add5 = add 5
697+
```
698+
663699
## Function Composition
664700

665701
Just like we were able to simplify the `insertEntry` function by using eta conversion, we can simplify the definition of `findEntry` by reasoning about its arguments.

0 commit comments

Comments
 (0)