Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 224 additions & 10 deletions doc/sjs-for-js/es6-to-scala-part3.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ more useful design patterns and features, to get you started quickly.

## Pattern matching

In the Basics part we already saw simple examples of _pattern matching_ as a replacement for JavaScript's `switch`
In the Basics part we already saw simple examples of *pattern matching* as a replacement for JavaScript's `switch`
statement. However, it can be used for much more, for example checking the type of input.

{% columns %}
Expand Down Expand Up @@ -51,8 +51,8 @@ def printType(o: Any): Unit = {
{% endcolumn %}
{% endcolumns %}

Pattern matching uses something called _partial functions_ which means it can be used in place of regular functions, for
example in a call to `filter` or `map`. You can also add a _guard clause_ in the form of an `if`, to limit the match. If
Pattern matching uses something called *partial functions* which means it can be used in place of regular functions, for
example in a call to `filter` or `map`. You can also add a *guard clause* in the form of an `if`, to limit the match. If
you need to match to a variable, use backticks to indicate that.

{% columns %}
Expand Down Expand Up @@ -260,10 +260,224 @@ Here we use triple-quoted strings that allow us to write regex without escaping
converted into a {% scaladoc util.matching.Regex %} object with the `.r` method. Because regexes extract strings, we need
to convert matched groups to integers ourselves.

## Functions revisited

We covered the basic use functions in [Part 1](es6-to-scala-part1.html), but Scala, being a functional programming
language, provides much more when it comes to functions. Let's explore some of the more advanced features and how they
compare to JavaScript.

#### Higher-order functions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be a level-3 header (and not 4)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 2 vs 4 level has been used elsewhere, too, for better visual clarity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK let's keep it that way for now, but we should globally fix this another time.


Scala, as JavaScript, allows the definition of higher-order functions. These are functions that take other functions as
parameters, or whose result is a function. Higher-order functions should be familiar to JavaScript developers, because
they often appear in form of functions that take callbacks as parameters.

Typically higher-order functions are used to pass specific functionality to a general function, like in the case of
`Array.prototype.filter` in ES6 or `Seq.filter` in Scala. We can use this to build a function to calculate a minimum and
maximum from a sequence of values, using a function to extract the target value.

{% columns %}
{% column 6 ES6 %}
{% highlight javascript %}
function minmaxBy(arr, f) {
return arr.reduce(
([min, max], e) => {
const v = f(e);
return [Math.min(min, v), Math.max(max, v)]
},
[Number.MAX_VALUE, Number.MIN_VALUE]
)
}
const [youngest, oldest] = minmaxBy(persons, e => e.age);
{% endhighlight %}
{% endcolumn %}

{% column 6 Scala %}
{% highlight scala %}
def minmaxBy[T](seq: Seq[T], f: T => Int): (Int, Int) = {
seq.foldLeft((Int.MaxValue, Int.MinValue)) {
case ((min, max), e) =>
val v = f(e)
(math.min(min, v), math.max(max, v))
}
}
val (youngest, oldest) = minmaxBy[Person](persons, _.age)
{% endhighlight %}
{% endcolumn %}
{% endcolumns %}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not convinced by the example. If I wanted this feature, I'd only write function average(arr), and then use average(persons.map(p => p.age)). avgBy looks contrived.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I modified the example to be a minmaxBy function which is not so easily constructed using existing functions (except by running over the sequence twice).


#### Call-by-Name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is too much. I love call-by-name parameters, but I feel like they live in an even more advanced space than the other features discussed on this page.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it's not advanced at all, just syntactic sugar for not needing to write closures and for simplifying the usage of a function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK


In some cases you want to *defer* the evaluation of a parameter value until when it's actually used in the function. For
this purpose Scala offers *call-by-name* parameters. This can be useful when dealing with an expensive computation that
is only optionally used by the function. In JavaScript the closest thing to this is to wrap a value in an anonymous
function with no arguments and pass *that* as a parameter, but that's more verbose and error-prone. You need to remember
to both wrap the value and call the function.

{% columns %}
{% column 6 ES6 %}
{% highlight javascript %}
function compute(value, cPos, cNeg) {
if (value >= 0)
return cPos();
else
return cNeg();
}

compute(x, () => expCalc(), () => expCalc2());
{% endhighlight %}
{% endcolumn %}

{% column 6 Scala %}
{% highlight scala %}
def compute(value: Int, cPos: => Int, cNeg: => Int) = {
if (value >= 0)
cPos
else
cNeg
}

compute(x, expCalc, expCalc2)
{% endhighlight %}
{% endcolumn %}
{% endcolumns %}

#### Recursive functions

Recursive functions can be very expressive, but they may also cause spurious stack overflows if the recursion gets too
deep. Scala automatically optimizes recursive functions that are *tail recursive*, allowing you to use them without
fear of overflowing the stack. To make sure your function is actually tail recursive, use the `@tailrec` annotation,
which will cause the Scala compiler to report an error if your function is not tail recursive.

Before ES6, JavaScript did not support tail call optimization, nor optimizing tail recursive functions. If you use a
smart ES6 transpiler, it can actually convert a tail recursive function into a `while` loop, but there are no checks
available to help you to verify the validity of tail recursion.

{% columns %}
{% column 6 ES6 %}
{% highlight javascript %}
function fib(n) {
function fibIter(n, next, prev) {
if (n === 0) {
return prev;
} else {
return fibIter(n - 1, next + prev, next);
}
};
return fibIter(n, 1, 0);
}
{% endhighlight %}
{% endcolumn %}

{% column 6 Scala %}
{% highlight scala %}
def fib(n: Int): Int = {
@tailrec
def fibIter(n: Int, next: Int, prev: Int): Int = {
if (n == 0)
prev
else
fibIter(n - 1, next + prev, next)
}
fibIter(n, 1, 0)
}
{% endhighlight %}
{% endcolumn %}
{% endcolumns %}


#### Partially applied functions

In Scala you can call a function with only some of its arguments and get back a function taking those missing arguments.
You do this by using `_` in place of the actual parameter. In JavaScript you can achieve the same by using the
`Function.prototype.bind` function (although it limits you to providing parameters from left to right). For example we
can define a function to create HTML tags by wrapping content within start and end tags.

{% columns %}
{% column 6 ES6 %}
{% highlight javascript %}
function tag(name, content) {
return `<${name}>${content}</${name}>`
}

const div = tag.bind(null, "div");
const p = tag.bind(null, "p");
const html = div(p("test")); // <div><p>test</p></div>
{% endhighlight %}
{% endcolumn %}

{% column 6 Scala %}
{% highlight scala %}
def tag(name: String, content: String) = {
s"<$name>$content</$name>"
}

val div = tag("div", _: String)
val p = tag("p", _: String)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

: String is not necessary, and I doubt it improves readability.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the _: String it is necessary, if that's what you meant.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come? tag is monomorphic function. Scala should be able to infer the type of _, shouldn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I would like to think too, but scalac begs to differ:

scala> val div = tag("div", _)
<console>:8: error: missing parameter type for expanded function ((x$1) => tag("div", x$1))
       val div = tag("div", _)

It works in val div: String => String = tag("div", _) but that's even wordier for this example.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch ... that's disappointing :(

val html = div(p("test")) // <div><p>test</p></div>
{% endhighlight %}
{% endcolumn %}
{% endcolumns %}

#### Multiple parameter lists
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is too much. Or not well motivated.

Using multiple parameter lists is mostly useful if the last argument is a function type, and you want to call it with {} syntax. For example in foldLeft. Using it for currying is comparatively rare.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this feature is used quite a lot by Scala collections and common Scala.js libs like Scalatags and scalajs-react, it makes sense to introduce it here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that's my point. It is mostly used to drive inference, for example in the collections library. For currying, it is not so much used. Hence, I think it should be motivated mainly by that use case, and not talk about currying (or talk about it after the last-param-is-a-function use case).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but currying is something that (sort of) exists in ES6, while type inference is Scala-only. That's why it feels more natural to start with a common topic and then go into the Scala specifics.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK


Scala allows a function to be defined with multiple parameter lists. In Scala this is quite common as it provides some
powerful secondary benefits besides the usual currying functionality. JavaScript does not directly support multiple
parameter lists in its syntax, but you can emulate it by returning a chain of functions, or by using libraries like
[lodash](https://lodash.com/docs#curry) that do it for you.

Let's use currying to define the `tag` function from previous example.

{% columns %}
{% column 6 ES6 %}
{% highlight javascript %}
function tag(name) {
return (content) => `<${name}>${content}</${name}>`;
}

const div = tag("div");
const p = tag("p");
const html = div(p("test")); // <div><p>test</p></div>
{% endhighlight %}
{% endcolumn %}

{% column 6 Scala %}
{% highlight scala %}
def tag(name: String)(content: String): String = {
s"<$name>$content</$name>"
}

val div = tag("div") _
val p = tag("p") _
val html = div(p("test")) // <div><p>test</p></div>
{% endhighlight %}
{% endcolumn %}
{% endcolumns %}

Multiple parameter lists also helps with *type inference*, meaning we don't need to tell the compiler the types
explicitly. For example we can rewrite the `minmaxBy` function as curried, which allows us to leave the `Person` type
out when calling it, as it is automatically inferred from the first parameter. This is why methods like `foldLeft` are
defined with multiple parameter lists.

{% columns %}
{% column 9 Scala %}
{% highlight scala %}
def minmaxBy[T](seq: Seq[T])(f: T => Int): (Int, Int) = {
seq.foldLeft((Int.MaxValue, Int.MinValue)) {
case ((min, max), e) =>
val v = f(e)
(math.min(min, v), math.max(max, v))
}
}
val (youngest, oldest) = minmaxBy(persons, _.age)
{% endhighlight %}
{% endcolumn %}
{% endcolumns %}

## Implicits

Being type safe is great in Scala, but sometimes the type system can be a bit prohibitive when you want to do something
else, like add methods to existing classes. To allow you to do this in a type safe manner, Scala provides _implicits_.
else, like add methods to existing classes. To allow you to do this in a type safe manner, Scala provides *implicits*.
You can think of implicits as something that's available in the scope when you need it, and the compiler can
automatically provide it. For example we can provide a function to automatically convert a JavaScript {% jsdoc Date %}
into a Scala/Java `Date`.
Expand Down Expand Up @@ -297,14 +511,14 @@ The monkey patching term became famous among Ruby developers and it has been ado
a way of extending existing classes with new methods. It has several pitfalls in dynamic languages and is generally
not a recommended practice. Especially dangerous is to patch JavaScript's host objects like `String` or `DOM.Node`. This
technique is, however, commonly used to provide support for new JavaScript functionality missing from older JS engines.
The practice is known as _polyfilling_ or _shimming_.
The practice is known as *polyfilling* or *shimming*.

In Scala providing extension methods via implicits is _perfectly safe_ and even a _recommended_ practice. The Scala
In Scala providing extension methods via implicits is *perfectly safe* and even a *recommended* practice. The Scala
standard library does it all the time. For example did you notice the `.r` or `.toInt` functions that were used on
strings in the regex example? Both are extension methods coming from implicit classes.

Let's use the `convertToDate` we defined before and add a `toDate` extension method to `String` by defining an _implicit
class_.
Let's use the `convertToDate` we defined before and add a `toDate` extension method to `String` by defining an *implicit
class*.

{% columns %}
{% column 6 ES6 %}
Expand All @@ -328,7 +542,7 @@ implicit class StrToDate(val s: String) {

Note that the JavaScript version modifies the global `String` class (dangerous!), whereas the Scala version only
introduces a conversion from `String` to a custom `StrToDate` class providing an additional method. Implicit classes are
_safe_ because they are lexically scoped, meaning the `StrToDate` is not available in other parts of the program unless
*safe* because they are lexically scoped, meaning the `StrToDate` is not available in other parts of the program unless
explicitly imported. The `toDate` method is not added to the `String` class in any way, instead the compiler generates
appropriate code to call it when required. Basically `"2010-10-09".toDate` is converted into `new
StrToDate("2010-10-09").toDate`.
Expand Down Expand Up @@ -390,7 +604,7 @@ images.sortBy(i => -i.width).take(10).foreach { i =>
## Futures

Writing asynchronous JavaScript code used to be painful due to the number of callbacks required to handle chained
asynchronous calls. This is affectionately known as _callback hell_. Then came the various `Promise` libraries that
asynchronous calls. This is affectionately known as *callback hell*. Then came the various `Promise` libraries that
alleviated this issue a lot, but were not fully compatible with each other. ES6 standardizes the {% jsdoc Promise %}
interface so that all implementations (ES6's own included) can happily coexist.

Expand Down