You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: text/chapter3.md
+47-3Lines changed: 47 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -479,6 +479,38 @@ This process is called _eta conversion_, and can be used (along with some other
479
479
480
480
In the case of `insertEntry`, _eta conversion_ has resulted in a very clear definition of our function - "`insertEntry` is just cons on lists". However, it is arguable whether point-free form is better in general.
481
481
482
+
## Property Accessors
483
+
484
+
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:
485
+
486
+
```haskell
487
+
\entry -> entry.address
488
+
```
489
+
490
+
PureScript provides an equivalent shorthand, where an underscore is followed by a field name, so the inline function above is equivalent to:
491
+
492
+
```haskell
493
+
_.address
494
+
```
495
+
496
+
This works with any number of levels or properties, so a function to extract the city associated with an `Entry` could be written as:
The last function we need to implement for our minimal address book application will look up a person by name and return the correct `Entry`. This will be a nice application of building programs by composing small functions - a key idea from functional programming.
@@ -563,7 +595,7 @@ Likewise, in the code for `findEntry` above, we used a different form of functio
563
595
564
596
This is equivalent to the usual application `head (filter filterEntry book)`
565
597
566
-
`($)` is just an alias for a regular function called `apply`, which is defined in the Prelude. It is defined as follows:
598
+
`$` is just an alias for a regular function called `apply`, which is defined in the Prelude. It is defined as follows:
567
599
568
600
```haskell
569
601
apply::forallab. (a->b) ->a->b
@@ -579,13 +611,24 @@ But why would we want to use `$` instead of regular function application? The re
579
611
For example, the following nested function application, which finds the street in the address of an employee's boss:
580
612
581
613
```haskell
582
-
street (address (boss employee))
614
+
_.street (_.address (_.boss employee))
583
615
```
584
616
585
617
becomes (arguably) easier to read when expressed using `$`:
586
618
587
619
```haskell
588
-
street $ address $ boss employee
620
+
_.street $ _.address $ _.boss employee
621
+
```
622
+
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
+
```
631
+
589
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:
590
633
```text
591
634
> mod 8 3
@@ -653,6 +696,7 @@ I will let you make your own decision which definition is easier to understand,
653
696
654
697
1. (Easy) Test your understanding of the `findEntry` function by writing down the types of each of its major subexpressions. For example, the type of the `head` function as used is specialized to `AddressBook -> Maybe Entry`. _Note_: There is no test for this exercise.
655
698
1. (Medium) Write a function `findEntryByStreet :: String -> AddressBook -> Maybe Entry` which looks up an `Entry` given a street address. _Hint_ reusing the existing code in `findEntry`. Test your function in PSCi and by running `spago test`.
699
+
1. (Medium) Rewrite `findEntryByStreet` to replace `filterEntry` with the composition (using `<<<` or `>>>`) of: a property accessor (using the `_.` notation); and a function that tests whether its given string argument is equal to the given street address.
656
700
1. (Medium) Write a function `isInBook` which tests whether a name appears in a `AddressBook`, returning a Boolean value. _Hint_: Use PSCi to find the type of the `Data.List.null` function, which tests whether a list is empty or not.
657
701
1. (Difficult) Write a function `removeDuplicates` which removes "duplicate" address book entries. We'll consider entries duplicated if they share the same first and last names, while ignoring `address` fields. _Hint_: Use PSCi to find the type of the `Data.List.nubBy` function, which removes duplicate elements from a list based on an equality predicate. Note that the first element in each set of duplicates (closest to list head) is the one that is kept.
0 commit comments