-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
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`. | ||
|
||
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._`). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a newbie, just knowing to say |
||
|
||
|
||
|
||
|
||
## Implicit conversion | ||
|
||
|
||
An implicit conversion happens the type check fails for an argument and an implicit conversion is found. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So here I need to explain two ways implicit conversions happen: |
||
``` | ||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
There was a problem hiding this comment.
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 thatgreet("Franchesca")
doesn't have the second parameter so it goes looking for an implicit parameter. Then you can say thatstandardGreeting
is found because it is animplicit val
.