# Implicit, to be or not to be

## 隐式转换是什么鬼

```1+"1"
```

"11"

```1 + "1"
```

## 隐式转换接受者

```implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
```

`implicit` 关键字代表后面这个 `class` 是隐式的，这样编译器在寻找隐式转换的时候就能找到它。

### 语法糖

```Map(1 -> "one", 2 -> "two", 3 -> "three")
```

1 -> "one" 其实是 tuple `(1, 2)` ，隐式转换帮我们实现这个箭头DSL

```implicit final class ArrowAssoc[A](private val self: A) extends AnyVal {
@inline def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y)
def →[B](y: B): Tuple2[A, B] = ->(y)
}
```

## 隐式转换参数

```class PreferredPrompt(val preference: String)
class PreferredDrink(val preference: String)

object Greeter {
def greet(name: String)(implicit prompt: PreferredPrompt,
drink: PreferredDrink) {

println("Welcome, "+ name +". The system is ready.")
print("But while you work, ")
println("why not enjoy a cup of "+ drink.preference +"?")
println(prompt.preference)
}
}
implicit val prompt = new PreferredPrompt("Yes, master> ")
implicit val drink = new PreferredDrink("tea")
Greeter.greet("Joe")
```
```Welcome, Joe. The system is ready.
But while you work, why not enjoy a cup of tea?
Yes, master>
```

### 类型类 Type Class

```trait Show[A] {
def show(f: A): String
}
def log[A](a: A)(implicit s: Show[A]) = println(s.show(a))
implicit val stringShow = new Show[String] {
def show(s: String) = s
}
log("a string")
```
```a string
Some(Some(hello))
```

• 类型类对应到scala中就是一个高阶 trait，这里既是 `Show[A]`
• 所以 `stringShow` 就是类型类 `Show``String` 实例。
• 当编译器看到 `log("a string")` 时，确定参数的类型 `A``String`
• 这样隐式参数的类型 `Show[A]` 也被定位到 `Show[String]`
• 编译器找到隐式转换 `Show[String]` 类型的 `stringShow` 并将其注入为 `s`

```trait Show[A] {
def show(f: A): String
}
def log[A](a: A)(implicit s: Show[A]) = println(s.show(a))
implicit val stringShow = new Show[String] {
def show(s: String) = s
}
implicit def optionShow[A](implicit sa: Show[A]) = new Show[Option[A]] {
def show(oa: Option[A]): String = oa match {
case None => "None"
case Some(a) => "Some("+ sa.show(a) + ")"
}
}
log(Option(Option("hello")))
```
```Some(Some(hello))
```
• 同样的 `log(Option(Option("hello")))` 首先确定 `A` 类型为 `Option[Option[String]]`
• `Show[Option[Option[String]]]` 的实例可以找到 `optionShow` ，确定 `A` 这时为 `Option[String]`
• 递归的，编译器又会找到 `optionShow`, 这次 `A``String``sa``stringShow` 注入

```def log[A: Show](a: A) = println(implicitly[Show[A]].show(a))
```
• `A:Show` 约束 `A``Show` 的一个实例
• `implicitly[Show[A]]` 会去寻找类型为 `Show[A]` 隐式转换

```log::(Show a) => a -> String
```

• 难以衔接的第三方库
• DSL

## or Not To Be

• 用得太多会影响可读性
• 如果继承，组合，重载能解决，最好别用隐式转换，但如果代码恶心又啰嗦，可以尝试使用隐式转换