From 7326fabe533ece0f6eb31839fd72ef9822dbdbff Mon Sep 17 00:00:00 2001 From: Shaun Lee Date: Sat, 2 Jan 2021 16:45:07 -0800 Subject: [PATCH 01/10] Add description of prefix functions versus infix operators. --- text/chapter3.md | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/text/chapter3.md b/text/chapter3.md index b7a02c40..85dcebc9 100644 --- a/text/chapter3.md +++ b/text/chapter3.md @@ -547,7 +547,19 @@ Note that, just like for top-level declarations, it was not necessary to specify ## Infix Function Application -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. +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: + +```text +> findEntry "John" "Smith" addressBook +``` + +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: + +```haskell +infix 4 eq as == +``` + +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. This is equivalent to the usual application `head (filter filterEntry book)` @@ -560,7 +572,7 @@ 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`. +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, low precedence operator. This means that `$` allows us to remove sets of parentheses for deeply-nested applications. @@ -574,6 +586,35 @@ becomes (arguably) easier to read when expressed using `$`: ```haskell street $ address $ boss employee +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: +```text +> mod 8 3 +2 +``` +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., +```text +> 8 `mod` 3 +2 +``` +Likewise, wrapping an operator in parentheses lets you use it as a function in prefix position: +```text +> 8 + 3 +11 + +> (+) 8 3 +11 +``` +This allows for compact definitions of curried (or partially applied) functions based on infix operator functions, such as the `add2` function below: +```text +> add2 = (+) 2 +> add2 4 +6 +``` +Alternatively, operators can be partially applied by surrounding them with parentheses and using `_` as an operand: +```text +> add3 = (3 + _) +> add3 2 +5 ``` ## Function Composition From 580ba9839990c183a4a5197c2cd7c8aadd15f101 Mon Sep 17 00:00:00 2001 From: Shaun Lee Date: Sat, 2 Jan 2021 16:45:51 -0800 Subject: [PATCH 02/10] Describe property accessors and add corresponding exercise. --- .../chapter3/test/no-peeking/Solutions.purs | 4 ++ text/chapter3.md | 50 +++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/exercises/chapter3/test/no-peeking/Solutions.purs b/exercises/chapter3/test/no-peeking/Solutions.purs index a77892bc..eb669a2d 100644 --- a/exercises/chapter3/test/no-peeking/Solutions.purs +++ b/exercises/chapter3/test/no-peeking/Solutions.purs @@ -12,6 +12,10 @@ findEntryByStreet streetName = filter filterEntry >>> head filterEntry :: Entry -> Boolean filterEntry e = e.address.street == streetName +-- Example alternative implementation using property accessor and composition +findEntryByStreet' :: String -> AddressBook -> Maybe Entry +findEntryByStreet' streetName = filter (_.address.street >>> ((==) streetName)) >>> head + isInBook :: String -> String -> AddressBook -> Boolean isInBook firstName lastName book = not null $ filter filterEntry book where diff --git a/text/chapter3.md b/text/chapter3.md index 85dcebc9..dfeacaf1 100644 --- a/text/chapter3.md +++ b/text/chapter3.md @@ -479,6 +479,38 @@ This process is called _eta conversion_, and can be used (along with some other 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. +## 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: + +```haskell +\entry -> entry.address +``` + +PureScript provides an equivalent shorthand, where an underscore is followed by a field name, so the inline function above is equivalent to: + +```haskell +_.address +``` + +This works with any number of levels or properties, so a function to extract the city associated with an `Entry` could be written as: + +```haskell +_.address.city +``` + +For example: + +```text +> address = { street: "123 Fake St.", city: "Faketown", state: "CA" } +> entry = { firstName: "John", lastName: "Smith", address: address } +> _.lastName entry +"Smith" + +> _.address.city entry +"Faketown" +``` + ## Querying the Address Book 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 This is equivalent to the usual application `head (filter filterEntry book)` -`($)` is just an alias for a regular function called `apply`, which is defined in the Prelude. It is defined as follows: +`$` is just an alias for a regular function called `apply`, which is defined in the Prelude. It is defined as follows: ```haskell apply :: forall a b. (a -> b) -> a -> b @@ -579,13 +611,24 @@ But why would we want to use `$` instead of regular function application? The re For example, the following nested function application, which finds the street in the address of an employee's boss: ```haskell -street (address (boss employee)) +_.street (_.address (_.boss employee)) ``` becomes (arguably) easier to read when expressed using `$`: ```haskell -street $ address $ boss employee +_.street $ _.address $ _.boss employee +``` + +Note that neither of the above examples is idiomatic PureScript. Real-world code is more likely to express this as: +```haskell +(boss employee).address.street +``` +or +```haskell +_.boss.address.street employee +``` + 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: ```text > mod 8 3 @@ -653,6 +696,7 @@ I will let you make your own decision which definition is easier to understand, 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. 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`. + 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. 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. 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. From 55c276b00aca0f10c6c06920d9f0e9906a0ceccf Mon Sep 17 00:00:00 2001 From: Shaun Date: Sat, 2 Jan 2021 19:25:38 -0800 Subject: [PATCH 03/10] Update text/chapter3.md Co-authored-by: milesfrain --- text/chapter3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/chapter3.md b/text/chapter3.md index dfeacaf1..af79278a 100644 --- a/text/chapter3.md +++ b/text/chapter3.md @@ -595,7 +595,7 @@ Likewise, in the code for `findEntry` above, we used a different form of functio This is equivalent to the usual application `head (filter filterEntry book)` -`$` is just an alias for a regular function called `apply`, which is defined in the Prelude. It is defined as follows: +`($)` is just an alias for a regular function called `apply`, which is defined in the Prelude. It is defined as follows: ```haskell apply :: forall a b. (a -> b) -> a -> b From c8e2105ea4f3dfbcedb65a9452c9c3a0713c24fc Mon Sep 17 00:00:00 2001 From: Shaun Date: Sat, 2 Jan 2021 19:25:52 -0800 Subject: [PATCH 04/10] Update exercises/chapter3/test/no-peeking/Solutions.purs Co-authored-by: milesfrain --- exercises/chapter3/test/no-peeking/Solutions.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/chapter3/test/no-peeking/Solutions.purs b/exercises/chapter3/test/no-peeking/Solutions.purs index eb669a2d..f5d1acd7 100644 --- a/exercises/chapter3/test/no-peeking/Solutions.purs +++ b/exercises/chapter3/test/no-peeking/Solutions.purs @@ -14,7 +14,7 @@ findEntryByStreet streetName = filter filterEntry >>> head -- Example alternative implementation using property accessor and composition findEntryByStreet' :: String -> AddressBook -> Maybe Entry -findEntryByStreet' streetName = filter (_.address.street >>> ((==) streetName)) >>> head +findEntryByStreet' streetName = filter (_.address.street >>> eq streetName) >>> head isInBook :: String -> String -> AddressBook -> Boolean isInBook firstName lastName book = not null $ filter filterEntry book From 0c5a2feb36aa8297197a19cb6e57fb8edd431d8a Mon Sep 17 00:00:00 2001 From: Shaun Date: Sat, 2 Jan 2021 19:26:21 -0800 Subject: [PATCH 05/10] Update text/chapter3.md Co-authored-by: milesfrain --- text/chapter3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/chapter3.md b/text/chapter3.md index af79278a..e9f877a6 100644 --- a/text/chapter3.md +++ b/text/chapter3.md @@ -487,7 +487,7 @@ One common pattern is to use a function to access an individual fields (or "prop \entry -> entry.address ``` -PureScript provides an equivalent shorthand, where an underscore is followed by a field name, so the inline function above is equivalent to: +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: ```haskell _.address From fbec12586061f58dd95a97815ad3e57b394fc216 Mon Sep 17 00:00:00 2001 From: Shaun Date: Sat, 2 Jan 2021 19:26:53 -0800 Subject: [PATCH 06/10] Update text/chapter3.md Co-authored-by: milesfrain --- text/chapter3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/chapter3.md b/text/chapter3.md index e9f877a6..67d3710d 100644 --- a/text/chapter3.md +++ b/text/chapter3.md @@ -604,7 +604,7 @@ 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`. +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, low precedence operator. This means that `$` allows us to remove sets of parentheses for deeply-nested applications. From f50148e14372c2c07c582de3b58a3cf87ec5a087 Mon Sep 17 00:00:00 2001 From: Shaun Date: Sun, 3 Jan 2021 10:41:19 -0800 Subject: [PATCH 07/10] Update text/chapter3.md Co-authored-by: milesfrain --- text/chapter3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/chapter3.md b/text/chapter3.md index 67d3710d..08d0ea23 100644 --- a/text/chapter3.md +++ b/text/chapter3.md @@ -653,7 +653,7 @@ This allows for compact definitions of curried (or partially applied) functions > add2 4 6 ``` -Alternatively, operators can be partially applied by surrounding them with parentheses and using `_` as an operand: +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): ```text > add3 = (3 + _) > add3 2 From 6f498f2198e87219e712269de42b683f6037bc1b Mon Sep 17 00:00:00 2001 From: Shaun Lee Date: Sun, 3 Jan 2021 11:47:01 -0800 Subject: [PATCH 08/10] Describe infix vs prefix based on AddressBook example. --- text/chapter3.md | 102 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 33 deletions(-) diff --git a/text/chapter3.md b/text/chapter3.md index 08d0ea23..9dee884c 100644 --- a/text/chapter3.md +++ b/text/chapter3.md @@ -579,18 +579,66 @@ Note that, just like for top-level declarations, it was not necessary to specify ## Infix Function Application -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: +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: -```text -> findEntry "John" "Smith" addressBook +```haskell +> book1 = insertEntry john emptyBook ``` -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: +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: ```haskell infix 4 eq as == ``` +and therefore `entry.firstName == firstName` in `filterEntry` could be replaced with the `eq entry.firstName firstName`. + +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: + +```text +> mod 8 3 +2 +``` + +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., + +```text +> 8 `mod` 3 +2 +``` + +In the same way, wrapping `insertEntry` in backticks turns it into an infix operator, such that `book1` and `book2` below are equivalent: + +```haskell +book1 = insertEntry john emptyBook +book2 = john `insertEntry` emptyBook +``` + +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: + +```haskell +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`: + +```haskell +infixr 5 insertEntry as ++ +``` + +This new operator lets us rewrite the above `book4` example as: + +```haskell +book6 = john ++ (peggy ++ (ned ++ emptyBook)) +``` + +and the right associativity of our new `++` operator lets us get rid of the parentheses without changing the meaning: + +```haskell +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. This is equivalent to the usual application `head (filter filterEntry book)` @@ -606,40 +654,22 @@ 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, low precedence operator. This means that `$` allows us to remove sets of parentheses for deeply-nested applications. +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 following nested function application, which finds the street in the address of an employee's boss: +For example, the above nested function application to create an `AddressBook` with three entries: ```haskell -_.street (_.address (_.boss employee)) +book3 = insertEntry john (insertEntry peggy (insertEntry ned emptyBook)) ``` becomes (arguably) easier to read when expressed using `$`: ```haskell -_.street $ _.address $ _.boss employee +book6 = insertEntry john $ insertEntry peggy $ insertEntry ned emptyBook ``` -Note that neither of the above examples is idiomatic PureScript. Real-world code is more likely to express this as: -```haskell -(boss employee).address.street -``` -or -```haskell -_.boss.address.street employee -``` +Wrapping an infix operator in parentheses lets you use it as a prefix function: -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: -```text -> mod 8 3 -2 -``` -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., -```text -> 8 `mod` 3 -2 -``` -Likewise, wrapping an operator in parentheses lets you use it as a function in prefix position: ```text > 8 + 3 11 @@ -647,19 +677,25 @@ Likewise, wrapping an operator in parentheses lets you use it as a function in p > (+) 8 3 11 ``` -This allows for compact definitions of curried (or partially applied) functions based on infix operator functions, such as the `add2` function below: -```text -> add2 = (+) 2 -> add2 4 -6 -``` + 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): + ```text > add3 = (3 + _) > add3 2 5 ``` +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 +``` + ## Function Composition 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. From cee541bafa7658d175f76fdf60b00c7ab3c3916d Mon Sep 17 00:00:00 2001 From: Miles Frain Date: Mon, 4 Jan 2021 00:55:50 -0800 Subject: [PATCH 09/10] Additional edits to #285 Wanted to make this a PR to a PR branch for easier commenting on proposed changes. https://github.com/purescript-contrib/purescript-book/pull/285 --- text/chapter3.md | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/text/chapter3.md b/text/chapter3.md index 9dee884c..de2b109c 100644 --- a/text/chapter3.md +++ b/text/chapter3.md @@ -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: ```haskell _.address @@ -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) where filterEntry :: Entry -> Boolean filterEntry entry = entry.firstName == firstName && entry.lastName == lastName @@ -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. 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: @@ -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 ++ @@ -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: @@ -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 +``` -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 @@ -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 parenthesis: ```text > 8 + 3 @@ -678,7 +675,7 @@ 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 + _) @@ -686,7 +683,7 @@ Alternatively, operators can be partially applied by surrounding them with paren 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 @@ -694,6 +691,9 @@ 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! ``` ## Function Composition From e17346afd4034e9ad6742ff1477ff920b3bcb7b9 Mon Sep 17 00:00:00 2001 From: Shaun Date: Mon, 4 Jan 2021 07:50:59 -0800 Subject: [PATCH 10/10] Update text/chapter3.md --- text/chapter3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/chapter3.md b/text/chapter3.md index de2b109c..4d32ef68 100644 --- a/text/chapter3.md +++ b/text/chapter3.md @@ -665,7 +665,7 @@ 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. -If you'd like to use a concise infix operator alias as a prefix function, you can surround it in parenthesis: +If you'd like to use a concise infix operator alias as a prefix function, you can surround it in parentheses: ```text > 8 + 3