隐含定义有多糟糕?

Dic*_*ici 8 scala implicit implicit-conversion

我喜欢隐含的定义.它们使代码看起来很好,它们使用户觉得某些特性在类只是一个隐式定义时自然可用.然而,我在考虑JS原型,你可以基本上在你没写的类上定义一个方法.但是如果在下一个版本中,这个类定义了一个具有相同签名的方法并对其行为做出假设,那么你就搞砸了.

Scala的含义使得几乎完全相同,只有一个主要区别:隐式定义是作用域的,因此类的作者不会有通过其他人的代码中的隐式定义注入代码的风险.但是用户的代码怎么样?是否可以保护他免受课堂上的变化?

我们考虑一下这段代码:

class HeyMan {
    def hello = println("Hello")
}

object Main extends App {
    val heyMan = new HeyMan

    implicit class ImplicitHeyMan(heyMan: HeyMan) {
        def hello = println("What's up ?")
    }
    heyMan.hello // prints Hello
}
Run Code Online (Sandbox Code Playgroud)

很糟糕,不是吗?对我来说,正确的行为应该是隐式定义总是隐藏真实定义,以便保护用户代码免受他所调用的API中新方法的出现.

你怎么看 ?有没有办法让它安全或者我们应该停止使用这种方式吗?

Dao*_*Wen 11

关于隐式转换的语言行为非常清楚地定义:

如果调用一个方法m的对象上o的一类C,而这个类不支持的方法m,然后将斯卡拉寻找从隐式转换C的东西,支持m.

http://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html

换句话说,一个隐式转换将永远不会被施加到heyMan在表达式heyMan.hello如果(静态已知)类/的性状heyMan已经定义了该方法hello只有当调用它的方法-implicit转换是试图已定义.


对我来说,正确的行为应该是隐式定义总是隐藏真实定义,以便保护用户代码免受他所调用的API中新方法的出现.

不是相反的情况同样如此吗?如果隐式转换确实优先,那么用户将面临他们长期定义的方法的危险,这些方法已经存在了5年,突然被新版本的库依赖中的新隐式转换所掩盖.

这种情况似乎很多更隐蔽,不易比用户的情况下调试明确的新方法的定义优先.


有没有办法让它安全或者我们应该停止使用这种方式吗?

如果获得隐式行为非常重要,也许您应该使用显式类型强制隐式转换:

object Main extends App {
    val heyMan = new HeyMan

    implicit class ImplicitHeyMan(heyMan: HeyMan) {
        def hello = println("What's up ?")
    }

    heyMan.hello // prints Hello

    val iHeyMan: ImplicitHeyMan // force conversion via implicit
    iHeyMan.hello // prints What's up
}
Run Code Online (Sandbox Code Playgroud)

从评论中的(扩展)对话中,您似乎想要一种方法来检查基础类是否不会通过隐式转换定义您正在使用的方法.

我认为Łukasz下面的评论是正确的 - 这是你应该在测试中捕获的东西.具体来说,您可以使用ScalaTestassertTypeError.只是尝试调用隐式范围之外的方法,它应该无法键入check(并传递测试):

// Should pass only if your implicit isn't in scope,
// and the underlying class doesn't define the hello method
assertTypeError("(new HeyMan).hello")
Run Code Online (Sandbox Code Playgroud)