DSL · 领域特定语言 · Domain-Specific Languages

scala隐式转换及其DSL实现(一)

2016-04-21  本文已影响707人  安静1337

隐式操作规则

隐式定义是指编译器为了修正类型错误而允许插入到程序中的定义。比如,x + y不能通过类型检查,此时,如果有某个可用的隐式转换convert,那么编译器会把它改成convert(x) + y。

隐式转换通用规则:

implicit def intToString (i: String) = i.toString

命名隐式转换:隐式转换可以任意命名,命名仅需要考虑两种情况,你是否在方法应用中明确写明,以及决定哪个隐式转换在程序的任何地方都有效。
比如第二点,对象带有两个隐式转换:

object Convert {
    implicit def stringWrapper(s: String): RandomAccessSeq[Char] = ...
    implicit def intToString(i: Int): String = ...
}

你可以使用像这样使用,从而有选择性的只引用一个:

import Convert.intToString
...

scala中能用到隐式操作的有三个地方:转换为期望类型指定(方法)调用者的转换隐式参数

隐式转换为期望类型

很简单,当编译器看见X时发生了错误,需要Y时,就会坚持从X到Y的隐式转换函数。
比如要修正 val i: Int = 3.5的错误,可以定义这样的隐式转换消除障碍:

implicit def doubleToInt (d: Double) = d.toInt

转换(方法调用的)接受者

隐式转换还应用于方法调用的接收者,也就是方法调用的对象。这种隐式转换主要有两种用途,一:接收者转换使得新的类可以更为平滑地集成到现存类层级中。二:支持编写域特定语言(DSL)。
1 与新类型的交互操作

class Number (n: Int) {
    def + (that: Number): Number = ...
    def + (that: Int): Number = ...
}

> val num = new Number(1)
/*
显然,num + 1表达式正确
但是1 + num就会抛出错误,
为了允许这种混合的运算需要定义Int到Number的隐式转换
*/
implicit def intToNumber (i: Int) = new Number(i)

> 1 + num //OK
/*
其实就是编译器检查失败后,搜索到了从Int到Number的类型转换,并应用了方法,等价于
intToNumber(1) + num
*/

2 模拟新的语法
scala里,可以这样创建Map:

Map(1 -> "one", 2 -> "two")

其实就是元组的写法,等价于:

Map((1, "one"), (2, "two"))

以下是实现的相关定义:

package scala
object Predef {
...
    class ArrowAssoc[A] (x: A) {
        def -> [B](y: B): Tuple2[A, B] = Tuple2(x, y)
    }
    implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x)
...
}

隐式参数

通过参数列表可以让编译器插入隐式操作,编译器有时会用call(a)(b)替代call(a), 或者用new obj(a)(b)替代new obj(a),从而通过添加缺失的参数列表以满足函数的调用,被提供的是完整的最后一节 柯里化 参数,而不仅是最后的参数,例如call(a)(b, c, d)替代call(a),并且这最后一组参数列表必须被标记为implicit。

class A (val name: String)
...
def func (id: Int)(implicit p: A) = ...

val a = new A("abc")
func(1)(a) //此时只能显示调用

implicit val aa = new A("cba")
func(1)(aa) or func(1)

scala隐式转换及其DSL实现(二)将介绍DSL实现的具体应用。

上一篇 下一篇

猜你喜欢

热点阅读