Skip to content

Rewrote implicits section of the tour #746

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
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
59 changes: 0 additions & 59 deletions tutorials/tour/_posts/2017-02-13-implicit-conversions.md

This file was deleted.

58 changes: 0 additions & 58 deletions tutorials/tour/_posts/2017-02-13-implicit-parameters.md

This file was deleted.

84 changes: 84 additions & 0 deletions tutorials/tour/_posts/2017-02-13-implicits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
layout: tutorial
title: Implicits

disqus: true

tutorial: scala-tour
categories: tour
num: 26
next-page: implicit-conversions
previous-page: explicitly-typed-self-references
---
Implicits allow for automatic application of code when an explicit application isn't supplied.

## Implicit parameters

Implicit parameters allow for the caller to omit an argument if an implicit one is in scope. Use the `implicit` keyword to make a value, object, or expression implicit. You also use it to make the parameter list implicit.
```tut
class Greeting(val greeting: String) {
def greet(name: String) = s"$greeting, $name"
}
implicit val standardGreeting = new Greeting("Hello")


def printGreeting(name: String)(implicit greeting: Greeting) = greeting.greet(name)


printGreeting("Franchesca") // Hello, Franchesca
printGreeting("Fred")(new Greeting("Good day")) // Good day, Fred
```
The `implicit val standardGreeting` is a value that can be supplied as an argument to an implicit parameter automatically. In the method `printGreeting`, the parameter `greeting` is implicit. This means that the caller can either supply an argument normally or skip it. With `greet("Franchesca")`, the compiler doesn't see a greeting but it notices that `greeting` is an implicit parameter so it searches the current scope for an implicit `Greeting` and finds `standardGreeting`.
Copy link

Choose a reason for hiding this comment

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

The first sentence is a little hard to understand. I would explain it the other way around--don't explain the implicit val standardGreeting until after you explain that greet("Franchesca") doesn't have the second parameter so it goes looking for an implicit parameter. Then you can say that standardGreeting is found because it is an implicit val.


This becomes useful when you have a lot of similar arguments to function calls throughout your program. However, implicits can make code more difficult to understand because it's not always obvious where they're defined if you import them from another module with a wildcard (e.g. `import MyPredef._`).
Copy link

Choose a reason for hiding this comment

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

This is absolutely not the main problem I have with implicits. The primary problem is that they hide an argument that can impact your program, making it less obvious what your methods depend upon because the method call doesn't show you that anything is going on! The secondary problem is that once you realize a method actually has an implicit parameter, it can sometimes be difficult to track down where it is because it might be from a wildcard import, not something you typed in.

Copy link
Contributor

Choose a reason for hiding this comment

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

As a newbie, just knowing to say import cats._ is the hard part.





## Implicit conversion


An implicit conversion happens the type check fails for an argument and an implicit conversion is found.
Copy link

@Ichoran Ichoran May 26, 2017

Choose a reason for hiding this comment

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

You need to explain what counts for executing an implicit conversion: an implicit def that converts a single argument of one type to another type; or an implicit val of a function that does the same thing.

Copy link
Contributor

Choose a reason for hiding this comment

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

So here I need to explain two ways implicit conversions happen:
-An implicit def that converts a single argument of one type to another type
-An implicit val of a function that does the same thing

```
def square(x: Double) = x * x
val number: Int = 5
square(number) // 25.0
```
This typecasting happens because of the method `implicit def int2double(x: Int): Double = x.toDouble` defined in `Predef` (a set of convenience methods in scope by default). When the compiler sees that `square` expects a `Double` but we pass an `Int`, it searches the scope for an `implicit` function that can do the conversion.
Copy link

Choose a reason for hiding this comment

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

Conversion of number types are a special case. I'd pick another example.


Implicits conversions can be useful when you're making a lot of calls to an API and the calls are verbose or require a type conversion. For example, if you want to create a button using Java swing, the code is verbose:
```tut
import scala.language.implicitConversions
import java.awt.event.{ActionEvent, ActionListener}
import javax.swing.JButton

val button = new JButton
button.addActionListener(
new ActionListener {
def actionPerformed(event: ActionEvent) = {
println("pressed!")
}
}
)
```
You would need to write this same code for every button in order to _print "pressed" when the button is pressed_. You could instead abstract this away to an implicit conversion:
```tut
import java.awt.event.{ActionEvent, ActionListener}
import javax.swing.JButton

implicit def function2ActionListener(f: ActionEvent => Unit) =
new ActionListener {
def actionPerformed(event: ActionEvent) = f(event)
}


val button = new JButton
button.addActionListener(
(_: ActionEvent) => println("pressed!")
)
```
The implicit method `function2ActionListener` takes a function which accepts an ActionEvent. It then returns an `ActionListener` with the aforementioned function as its `actionPerformed`. Now when we call `button.addActionListener` (which accepts an `ActionListener`) with an anonymous function of type `ActionEvent => Unit`, the compiler looks for an implicit conversion function which can convert the type to `ActionListener`.

This removes a lot of the boilerplate because we can use an anonymous function. However, because implicits are often defined outside of the package, it can be difficult to debug. Therefore they are best used in libraries.

Copy link

Choose a reason for hiding this comment

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

What about implicit classes?

_Example Credit_: Odersky, Martin, Lex Spoon, and Bill Venners. Programming in Scala. Walnut Creek, CA: Artima, 2016.