From 22ef474bc6e8bff4e3762395e43f6324a299c06d Mon Sep 17 00:00:00 2001 From: William Raendchen <105014007+epic-64@users.noreply.github.com> Date: Sat, 21 Dec 2024 18:48:13 +0100 Subject: [PATCH 1/2] clarify purity in the context of closures --- _overviews/scala3-book/fp-pure-functions.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/_overviews/scala3-book/fp-pure-functions.md b/_overviews/scala3-book/fp-pure-functions.md index 641eee59ce..e2e4d32cda 100644 --- a/_overviews/scala3-book/fp-pure-functions.md +++ b/_overviews/scala3-book/fp-pure-functions.md @@ -13,7 +13,7 @@ Another feature that Scala offers to help you write functional code is the abili A _pure function_ can be defined like this: - A function `f` is pure if, given the same input `x`, it always returns the same output `f(x)` -- The function’s output depends _only_ on its input variables and its implementation +- The function’s output depends _only_ on its input variables and its implementation (and in case of a closure, any immutable data that it captures) - It only computes the output and does not modify the world around it This implies: @@ -57,7 +57,7 @@ Conversely, the following functions are _impure_ because they violate the defini Impure functions often do one or more of these things: -- Read from hidden state, i.e., they access variables and data not explicitly passed into the function as input parameters +- Read from hidden mutable state, i.e., they access non-constant variables and data not explicitly passed into the function as input parameters. - Write to hidden state - Mutate the parameters they’re given, or mutate hidden variables, such as fields in their containing class - Perform some sort of I/O with the outside world @@ -98,6 +98,22 @@ def double(i: Int): Int = i * 2 {% endtabs %} +The next example is bit more tricky. Here, i is not passed as a parameter, but instead referenced directly from the function body. +This works in Scala because functions act as closure - they can capture the state around them. As long as that state is *immutable*, the function is still considered pure. +In this case, the function always returns `6` and each call could be safely replaced with its result. +This concept of closures and "fixing values" is an important tool in functional programming that you will encounter often as you go forward. + +{% tabs fp-pure-function-closure %} + +{% tab 'Scala 2 and 3' %} +```scala +val i = 3 +def double(i: Int): Int = i * 2 +``` +{% endtab %} + +{% endtabs %} + If you’re comfortable with recursion, here’s a pure function that calculates the sum of a list of integers: {% tabs fp-pure-recursive-function class=tabs-scala-version %} From 2e29269b8d8b20137f4efc2c6778e1bc87395b87 Mon Sep 17 00:00:00 2001 From: William Raendchen <105014007+epic-64@users.noreply.github.com> Date: Sat, 21 Dec 2024 19:04:11 +0100 Subject: [PATCH 2/2] fix typos --- _overviews/scala3-book/fp-pure-functions.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/_overviews/scala3-book/fp-pure-functions.md b/_overviews/scala3-book/fp-pure-functions.md index e2e4d32cda..e188ce3a39 100644 --- a/_overviews/scala3-book/fp-pure-functions.md +++ b/_overviews/scala3-book/fp-pure-functions.md @@ -57,7 +57,7 @@ Conversely, the following functions are _impure_ because they violate the defini Impure functions often do one or more of these things: -- Read from hidden mutable state, i.e., they access non-constant variables and data not explicitly passed into the function as input parameters. +- Read from hidden mutable state, i.e., they access non-constant data that was not explicitly passed into the function as input parameters - Write to hidden state - Mutate the parameters they’re given, or mutate hidden variables, such as fields in their containing class - Perform some sort of I/O with the outside world @@ -98,17 +98,16 @@ def double(i: Int): Int = i * 2 {% endtabs %} -The next example is bit more tricky. Here, i is not passed as a parameter, but instead referenced directly from the function body. -This works in Scala because functions act as closure - they can capture the state around them. As long as that state is *immutable*, the function is still considered pure. -In this case, the function always returns `6` and each call could be safely replaced with its result. -This concept of closures and "fixing values" is an important tool in functional programming that you will encounter often as you go forward. +The next example is bit more tricky. Here, `i` is not passed as a parameter, but instead referenced directly from the outside. +This works in Scala because functions act as closures - they can capture the state around them. As long as that state is *immutable*, such a closure is still considered pure. +In this case, the function always returns `6` and each call can be safely replaced with its result. {% tabs fp-pure-function-closure %} {% tab 'Scala 2 and 3' %} ```scala val i = 3 -def double(i: Int): Int = i * 2 +def double(): Int = i * 2 ``` {% endtab %} @@ -145,7 +144,7 @@ If you understand that code, you’ll see that it meets the pure function defini The first key point of this section is the definition of a pure function: -> A _pure function_ is a function that depends only on its declared inputs and its implementation to produce its output. +> A _pure function_ is a function that depends only on its declared inputs, captured constants, and its implementation to produce its output. > It only computes its output and does not depend on or modify the outside world. A second key point is that every real-world application interacts with the outside world.