Skip to content

Commit

Permalink
Merge pull request #2625 from bernit77/add_code_tab_fun_write_map_fun…
Browse files Browse the repository at this point in the history
…ction

Add code tabs in scala3/book/fun-write-map-function
  • Loading branch information
julienrf authored Nov 3, 2022
2 parents cbb493e + c668af2 commit ee7148c
Showing 1 changed file with 51 additions and 3 deletions.
54 changes: 51 additions & 3 deletions _overviews/scala3-book/fun-write-map-function.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,68 +20,116 @@ Focusing only on a `List[Int]`, you state:
Given that statement, you start to write the method signature.
First, you know that you want to accept a function as a parameter, and that function should transform an `Int` into some generic type `A`, so you write:

{% tabs map-accept-func-definition %}
{% tab 'Scala 2 and 3' %}
```scala
def map(f: (Int) => A)
```
{% endtab %}
{% endtabs %}

The syntax for using a generic type requires declaring that type symbol before the parameter list, so you add that:

{% tabs map-type-symbol-definition %}
{% tab 'Scala 2 and 3' %}
```scala
def map[A](f: (Int) => A)
```
{% endtab %}
{% endtabs %}

Next, you know that `map` should also accept a `List[Int]`:

{% tabs map-list-int-param-definition %}
{% tab 'Scala 2 and 3' %}
```scala
def map[A](f: (Int) => A, xs: List[Int])
```
{% endtab %}
{% endtabs %}

Finally, you also know that `map` returns a transformed `List` that contains elements of the generic type `A`:

{% tabs map-with-return-type-definition %}
{% tab 'Scala 2 and 3' %}
```scala
def map[A](f: (Int) => A, xs: List[Int]): List[A] = ???
```
{% endtab %}
{% endtabs %}

That takes care of the method signature.
Now all you have to do is write the method body.
A `map` method applies the function it’s given to every element in the list it’s given to produce a new, transformed list.
One way to do this is with a `for` expression:

{% tabs for-definition class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
for (x <- xs) yield f(x)
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
for x <- xs yield f(x)
```
{% endtab %}
{% endtabs %}

`for` expressions often make code surprisingly simple, and for our purposes, that ends up being the entire method body.

Putting it together with the method signature, you now have a standalone `map` method that works with a `List[Int]`:

{% tabs map-function class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
def map[A](f: (Int) => A, xs: List[Int]): List[A] =
for (x <- xs) yield f(x)
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
def map[A](f: (Int) => A, xs: List[Int]): List[A] =
for x <- xs yield f(x)
```
{% endtab %}
{% endtabs %}


### Make it generic

As a bonus, notice that the `for` expression doesn’t do anything that depends on the type inside the `List` being `Int`.
Therefore, you can replace `Int` in the type signature with the generic type parameter `B`:

{% tabs map-function-full-generic class=tabs-scala-version %}
{% tab 'Scala 2' %}
```scala
def map[A, B](f: (B) => A, xs: List[B]): List[A] =
for (x <- xs) yield f(x)
```
{% endtab %}
{% tab 'Scala 3' %}
```scala
def map[A, B](f: (B) => A, xs: List[B]): List[A] =
for x <- xs yield f(x)
```
{% endtab %}
{% endtabs %}

Now you have a `map` method that works with any `List`.

These examples demonstrate that `map` works as desired:

{% tabs map-use-example %}
{% tab 'Scala 2 and 3' %}
```scala
def double(i : Int) = i * 2
def double(i : Int): Int = i * 2
map(double, List(1, 2, 3)) // List(2, 4, 6)

def strlen(s: String) = s.length
def strlen(s: String): Int = s.length
map(strlen, List("a", "bb", "ccc")) // List(1, 2, 3)
```
{% endtab %}
{% endtabs %}

Now that you’ve seen how to write methods that accept functions as input parameters, let’s look at methods that return functions.

Expand Down

0 comments on commit ee7148c

Please sign in to comment.