From 50f9000ee6a862d1795234a3d322a1472fea4190 Mon Sep 17 00:00:00 2001 From: Travis Scala Date: Fri, 10 Feb 2017 15:04:13 +0100 Subject: [PATCH 1/6] rewrite v0 --- tutorials/tour/higher-order-functions.md | 115 +++++++++++++++++++---- 1 file changed, 98 insertions(+), 17 deletions(-) diff --git a/tutorials/tour/higher-order-functions.md b/tutorials/tour/higher-order-functions.md index a042b9625d..433400ce85 100644 --- a/tutorials/tour/higher-order-functions.md +++ b/tutorials/tour/higher-order-functions.md @@ -8,34 +8,115 @@ tutorial: scala-tour num: 8 next-page: nested-functions previous-page: anonymous-function-syntax +assumed-knowledge: sequence-comprehensions --- -Scala allows the definition of higher-order functions. These are functions that _take other functions as parameters_, or whose _result is a function_. Here is a function `apply` which takes another function `f` and a value `v` and applies function `f` to `v`: +Higher order functions are functions that take other functions are parameters +or whose result is a function. They can be useful for reducing duplicate code +when you want to create multiple functions with only slightly different functionality. +One of the most common examples is the higher-order +function `map` available for collections in Scala. ```tut -def apply(f: Int => String, v: Int) = f(v) +val salaries = Seq(20000, 70000, 40000) +val doubleSalary = (x: Int) => x * 2 +val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000) ``` +In this case, the function `doubleSalary` gets applied to each element in the +list of salaries. A more idiomatic way to write the same piece of code would be -_Note: methods are automatically coerced to functions if the context requires this._ - -Here is another example: - ```tut -class Decorator(left: String, right: String) { - def layout[A](x: A) = left + x.toString() + right -} +val salaries = Seq(20000, 70000, 40000) +val newSalaries = salaries.map(_ * 2) +``` +The Scala compiler already knows the type of the parameters (a single Int) so +you just need to provide the right side of the function doubleSalary. The only +caveat is that you need to use `_` in place of a parameter name (it was `x` in +the previous example). -object FunTest extends App { - def apply(f: Int => String, v: Int) = f(v) - val decorator = new Decorator("[", "]") - println(apply(decorator.layout, 7)) +## Coercing methods into functions +It is also possible to pass methods as arguments to higher-order functions because +the Scala compiler will coerce the method into a function. +``` +case class WeeklyWeatherForecast(temperatures: Seq[Double]) { + + private def convertCtoF(temp: Double) = temp * 1.8 + 32 + + def getForecastInFahrenheit() = temperatures.map(convertCtoF) // <-- passing the method convertCtoF } ``` - -Execution yields the output: +Here the method `convertCtoF` is passed to getForecastInFahrenheit This is possible because the compiler coerces `convertCtoF` to `_ => convertCtoF(_)`, i.e. a function. + +## Functions that accept functions +Let's say you wanted a function that could either search for files by directory, +by regular expression, or by a substring. +by file name, extension, or by contents. Without creating a higher-order function, +it might look something like this: +```tut + object FileFinder { + private def filesHere = (new java.io.File(".")).listFiles + + def filesEndingWith(query: String) = + for (file <- filesHere; if file.getName.endsWith(query)) + yield + + def filesContaining(query: String) = + for (file <- filesHere; if file.getName.contains(query)) + yield file + + def filesMatchingRegex(query: String) = + for (file <- filesHere; if file.getName.matches(query)) + yield file + } ``` -[7] + +Notice how each of the three methods vary only by a single method call. To simplify, +you can extract the repeated code into a higher-order function like so: + +```tut +object FileMatcher { + private def filesHere = (new java.io.File(".")).listFiles + + private def filesMatching(matcher: String => Boolean) = + for (file <- filesHere; if matcher(file.getName)) + yield file + + def filesEndingWith(query: String) = + filesMatching(_.endsWith(query)) + + def filesContaining(query: String) = + filesMatching(_.contains(query)) + + def filesRegex(query: String) = + filesMatching(_.matches(query)) + } ``` -In this example, the method `decorator.layout` is coerced automatically to a value of type `Int => String` as required by method `apply`. Please note that method `decorator.layout` is a _polymorphic method_ (i.e. it abstracts over some of its signature types) and the Scala compiler has to instantiate its method type first appropriately. +The new function, `filesMatching`, takes a function of type `String => Boolean` +(i.e. a function that takes a String and returns a Boolean) and returns the Sequence comprehension +created by the `for`/`yield`. + +Credit: Odersky, Martin, Lex Spoon, and Bill Venners. Programming in Scala. Walnut Creek, CA: Artima, 2010. Web. + +## Functions that return functions +There are certain cases where you +```tut +def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = { + val schema = if (ssl) "https://" else "http://" + (endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query" +} +​ +val domainName = "www.example.com" +def getURL = urlBuilder(ssl=true, domainName) +​ +val endpoint = "users" +val query = "id=1" +getURL(endpoint, query) // "https://www.example.com/users?id=1": String +​``` + + +Caveat +```tut +List(1,3,4).map(MyNum(_)) +``` From 1c534f1b3e71dd1de09a86318e7a7fa5f234f68c Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 10 Feb 2017 15:45:26 +0100 Subject: [PATCH 2/6] Finished sentences --- tutorials/tour/higher-order-functions.md | 33 ++++++++++-------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/tutorials/tour/higher-order-functions.md b/tutorials/tour/higher-order-functions.md index 433400ce85..6c1536e82d 100644 --- a/tutorials/tour/higher-order-functions.md +++ b/tutorials/tour/higher-order-functions.md @@ -11,12 +11,10 @@ previous-page: anonymous-function-syntax assumed-knowledge: sequence-comprehensions --- -Higher order functions are functions that take other functions are parameters -or whose result is a function. They can be useful for reducing duplicate code -when you want to create multiple functions with only slightly different functionality. - +Higher order functions take other functions as parameters or return a function as +a result. This is possible because functions are first-class objects in Scala. One of the most common examples is the higher-order -function `map` available for collections in Scala. +function `map` which is available for collections in Scala. ```tut val salaries = Seq(20000, 70000, 40000) val doubleSalary = (x: Int) => x * 2 @@ -29,8 +27,8 @@ list of salaries. A more idiomatic way to write the same piece of code would be val salaries = Seq(20000, 70000, 40000) val newSalaries = salaries.map(_ * 2) ``` -The Scala compiler already knows the type of the parameters (a single Int) so -you just need to provide the right side of the function doubleSalary. The only +The Scala compiler already knows the type of the parameters (a single Int) that function argument +for `map` needs. Therefore you just need to provide the right side of the function `doubleSalary`. The only caveat is that you need to use `_` in place of a parameter name (it was `x` in the previous example). @@ -48,9 +46,7 @@ case class WeeklyWeatherForecast(temperatures: Seq[Double]) { Here the method `convertCtoF` is passed to getForecastInFahrenheit This is possible because the compiler coerces `convertCtoF` to `_ => convertCtoF(_)`, i.e. a function. ## Functions that accept functions -Let's say you wanted a function that could either search for files by directory, -by regular expression, or by a substring. -by file name, extension, or by contents. Without creating a higher-order function, +One reason to use higher-order functions is to reduce redundant code. Let's say you wanted functions that could either search for files by directory, by regular expression, or by a substring. Without creating a higher-order function, it might look something like this: ```tut @@ -88,7 +84,7 @@ object FileMatcher { def filesContaining(query: String) = filesMatching(_.contains(query)) - def filesRegex(query: String) = + def filesMatchingRegex(query: String) = filesMatching(_.matches(query)) } ``` @@ -100,7 +96,10 @@ created by the `for`/`yield`. Credit: Odersky, Martin, Lex Spoon, and Bill Venners. Programming in Scala. Walnut Creek, CA: Artima, 2010. Web. ## Functions that return functions -There are certain cases where you + +There are certain cases where you want to generate a function. Here's an example +of a method that returns a function. + ```tut def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = { val schema = if (ssl) "https://" else "http://" @@ -112,11 +111,7 @@ def getURL = urlBuilder(ssl=true, domainName) ​ val endpoint = "users" val query = "id=1" -getURL(endpoint, query) // "https://www.example.com/users?id=1": String +val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String ​``` - - -Caveat -```tut -List(1,3,4).map(MyNum(_)) -``` +Notice the return type of urlBuilder `(String, String) => String`. This means that +the returned anonymous function takes two Strings and returns a String. From 8ce1a150553eef5f23f33242d8ce0afbc11b30c0 Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 10 Feb 2017 15:54:20 +0100 Subject: [PATCH 3/6] coercion --- tutorials/tour/higher-order-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/tour/higher-order-functions.md b/tutorials/tour/higher-order-functions.md index 6c1536e82d..8c9b36d4a9 100644 --- a/tutorials/tour/higher-order-functions.md +++ b/tutorials/tour/higher-order-functions.md @@ -43,7 +43,7 @@ case class WeeklyWeatherForecast(temperatures: Seq[Double]) { def getForecastInFahrenheit() = temperatures.map(convertCtoF) // <-- passing the method convertCtoF } ``` -Here the method `convertCtoF` is passed to getForecastInFahrenheit This is possible because the compiler coerces `convertCtoF` to `_ => convertCtoF(_)`, i.e. a function. +Here the method `convertCtoF` is passed to getForecastInFahrenheit This is possible because the compiler coerces `convertCtoF` to `_ => convertCtoF(_)`, which is a function. ## Functions that accept functions One reason to use higher-order functions is to reduce redundant code. Let's say you wanted functions that could either search for files by directory, by regular expression, or by a substring. Without creating a higher-order function, From b8b5c0d50d6a00bf5476aba59ed2304026f199e5 Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 10 Feb 2017 15:58:27 +0100 Subject: [PATCH 4/6] formatting --- tutorials/tour/higher-order-functions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tutorials/tour/higher-order-functions.md b/tutorials/tour/higher-order-functions.md index 8c9b36d4a9..f6e9aed327 100644 --- a/tutorials/tour/higher-order-functions.md +++ b/tutorials/tour/higher-order-functions.md @@ -113,5 +113,6 @@ val endpoint = "users" val query = "id=1" val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String ​``` + Notice the return type of urlBuilder `(String, String) => String`. This means that the returned anonymous function takes two Strings and returns a String. From 37b187468fdca074b658b7774e321d3c07ecda76 Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 10 Feb 2017 15:59:14 +0100 Subject: [PATCH 5/6] formatting --- tutorials/tour/higher-order-functions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tutorials/tour/higher-order-functions.md b/tutorials/tour/higher-order-functions.md index f6e9aed327..08897fb758 100644 --- a/tutorials/tour/higher-order-functions.md +++ b/tutorials/tour/higher-order-functions.md @@ -112,7 +112,8 @@ def getURL = urlBuilder(ssl=true, domainName) val endpoint = "users" val query = "id=1" val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String -​``` +​ +``` Notice the return type of urlBuilder `(String, String) => String`. This means that the returned anonymous function takes two Strings and returns a String. From f68ef92a089b5a459a618f98cadab8bb9aa73897 Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Sun, 12 Feb 2017 18:58:07 +0100 Subject: [PATCH 6/6] reworded coercion --- tutorials/tour/higher-order-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/tour/higher-order-functions.md b/tutorials/tour/higher-order-functions.md index 08897fb758..81f1b9846a 100644 --- a/tutorials/tour/higher-order-functions.md +++ b/tutorials/tour/higher-order-functions.md @@ -43,7 +43,7 @@ case class WeeklyWeatherForecast(temperatures: Seq[Double]) { def getForecastInFahrenheit() = temperatures.map(convertCtoF) // <-- passing the method convertCtoF } ``` -Here the method `convertCtoF` is passed to getForecastInFahrenheit This is possible because the compiler coerces `convertCtoF` to `_ => convertCtoF(_)`, which is a function. +Here the method `convertCtoF` is passed to getForecastInFahrenheit This is possible because the compiler coerces `convertCtoF` to the function `_ => convertCtoF(_)`. ## Functions that accept functions One reason to use higher-order functions is to reduce redundant code. Let's say you wanted functions that could either search for files by directory, by regular expression, or by a substring. Without creating a higher-order function,