避免 Scala 中隐式 def 歧义

发布于 2024-08-03 21:45:18 字数 1182 浏览 4 评论 0原文

我正在尝试创建从任何类型(例如 Int)到 String 的隐式转换...

到 String 的隐式转换意味着 RichString 方法(如反向)不可用。

implicit def intToString(i: Int) = String.valueOf(i)
100.toCharArray  // => Array[Char] = Array(1, 0, 0)
100.reverse // => error: value reverse is not a member of Int
100.length // => 3

到 RichString 的隐式转换意味着 String 方法(如 toCharArray)不可用。同时

implicit def intToRichString(i: Int) = new RichString(String.valueOf(i))
100.reverse // => "001"
100.toCharArray  // => error: value toCharArray is not a member of Int
100.length // => 3

使用这两种隐式转换意味着重复的方法(如 length)不明确。

implicit def intToString(i: Int) = String.valueOf(i)
implicit def intToRichString(i: Int) = new RichString(String.valueOf(i))
100.toCharArray  // => Array[Char] = Array(1, 0, 0)
100.reverse // => "001"
100.length // => both method intToString in object $iw of type 
   // (Int)java.lang.String and method intToRichString in object
   // $iw of type (Int)scala.runtime.RichString are possible 
   // conversion functions from Int to ?{val length: ?}

那么,是否可以隐式转换为 String 并且仍然支持所有 String 和 RichString 方法?

I am trying to create an implicit conversion from any type (say, Int) to a String...

An implicit conversion to String means RichString methods (like reverse) are not available.

implicit def intToString(i: Int) = String.valueOf(i)
100.toCharArray  // => Array[Char] = Array(1, 0, 0)
100.reverse // => error: value reverse is not a member of Int
100.length // => 3

An implicit conversion to RichString means String methods (like toCharArray) are not available

implicit def intToRichString(i: Int) = new RichString(String.valueOf(i))
100.reverse // => "001"
100.toCharArray  // => error: value toCharArray is not a member of Int
100.length // => 3

Using both implicit conversions means duplicated methods (like length) are ambiguous.

implicit def intToString(i: Int) = String.valueOf(i)
implicit def intToRichString(i: Int) = new RichString(String.valueOf(i))
100.toCharArray  // => Array[Char] = Array(1, 0, 0)
100.reverse // => "001"
100.length // => both method intToString in object $iw of type 
   // (Int)java.lang.String and method intToRichString in object
   // $iw of type (Int)scala.runtime.RichString are possible 
   // conversion functions from Int to ?{val length: ?}

So, is it possible to implicitly convert to String and still support all String and RichString methods?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

尬尬 2024-08-10 21:45:18

我没有解决方案,但会评论说,在 intToString 隐式之后 RichString 方法不可用的原因是 Scala 不会链接隐式调用(请参阅 21.2“规则”)对于隐式”Scala 编程)。

如果引入中间 String,Scala 将隐式转换为 RichString(该隐式转换在 Predef.scala 中定义)。

例如,

$ scala
Welcome to Scala version 2.7.5.final [...].
Type in expressions to have them evaluated.
Type :help for more information.

scala> implicit def intToString(i: Int) = String.valueOf(i)
intToString: (Int)java.lang.String

scala> val i = 100
i: Int = 100

scala> val s: String = i
s: String = 100

scala> s.reverse
res1: scala.runtime.RichString = 001

I don't have a solution, but will comment that the reason RichString methods are not available after your intToString implicit is that Scala does not chain implicit calls (see 21.2 "Rules for implicits" in Programming in Scala).

If you introduce an intermediate String, Scala will make the implict converstion to a RichString (that implicit is defined in Predef.scala).

E.g.,

$ scala
Welcome to Scala version 2.7.5.final [...].
Type in expressions to have them evaluated.
Type :help for more information.

scala> implicit def intToString(i: Int) = String.valueOf(i)
intToString: (Int)java.lang.String

scala> val i = 100
i: Int = 100

scala> val s: String = i
s: String = 100

scala> s.reverse
res1: scala.runtime.RichString = 001
勿忘初心 2024-08-10 21:45:18

从 Scala 2.8 开始,这一点得到了改进。根据 本文(§避免歧义):

以前,最具体的重载方法或隐式转换
将仅根据方法的参数类型进行选择。有一个
附加子句表示最具体的方法不能是
在任何其他替代方案的适当超类中定义。这
Scala 2.8 中的方案已被以下更自由的方案取代:
当比较重载的两种不同的适用替代方案时
方法或隐式方法,每个方法因拥有更多方法而得一分
具体论点,以及在适当的情况下定义的另一点
子类。如果一个替代方案获得更多数量,则它会“胜过”另一个方案
这两次比较的要点。 这特别意味着如果
替代方案具有相同的参数类型,即在 a 中定义的参数类型
子类获胜。

请参阅另一篇论文(§6.5) )为例。

As of Scala 2.8, this has been improved. As per this paperAvoiding Ambiguities) :

Previously, the most specific overloaded method or implicit conversion
would be chosen based solely on the method’s argument types. There was an
additional clause which said that the most specific method could not be
defined in a proper superclass of any of the other alternatives. This
scheme has been replaced in Scala 2.8 by the following, more liberal one:
When comparing two different applicable alternatives of an overloaded
method or of an implicit, each method gets one point for having more
specific arguments, and another point for being defined in a proper
subclass. An alternative “wins” over another if it gets a greater number
of points in these two comparisons. This means in particular that if
alternatives have identical argument types, the one which is defined in a
subclass wins.

See that other paper (§6.5) for an example.

旧情勿念 2024-08-10 21:45:18

要么创建一个巨大的代理类,要么将其吸收并要求客户端消除歧义:

100.asInstanceOf[String].length

Either make a huge proxy class, or suck it up and require the client to disambiguate it:

100.asInstanceOf[String].length

忘年祭陌 2024-08-10 21:45:18

我看到的唯一选择是创建一个新的字符串包装类 MyString 并让它调用您想要在不明确的情况下调用的任何方法。然后,您可以定义到 MyString 的隐式转换以及从 MyString 到 String 和 RichString 的两个隐式转换,以防万一您需要将其传递给库函数。

The only option I see is to create a new String Wrapper class MyString and let that call whatever method you want to be called in the ambiguous case. Then you could define implicit conversions to MyString and two implicit conversions from MyString to String and RichString, just in case you need to pass it to a library function.

路还长,别太狂 2024-08-10 21:45:18

接受的解决方案(由 Mitch Blevins 发布)永远不会起作用:使用 asInstanceOfInt 向下转换为 String 将始终失败。

解决您的问题的一种方法是添加从任何字符串可转换类型到 RichString 的转换(或者更确切地说,添加到 StringOps 的转换,因为它现在被命名为):

implicit def stringLikeToRichString[T](x: T)(implicit conv: T => String) = new collection.immutable.StringOps(conv(x))

然后定义您的转换( s) 像以前一样串起来:

scala> implicit def intToString(i: Int) = String.valueOf(i)
warning: there was one feature warning; re-run with -feature for details
intToString: (i: Int)String

scala> 100.toCharArray
res0: Array[Char] = Array(1, 0, 0)

scala> 100.reverse
res1: String = 001

scala> 100.length
res2: Int = 3

The accepted solution (posted by Mitch Blevins) will never work: downcasting Int to String using asInstanceOf will always fail.

One solution to your problem is to add a conversion from any String-convertible type to RichString (or rather, to StringOps as it is now named):

implicit def stringLikeToRichString[T](x: T)(implicit conv: T => String) = new collection.immutable.StringOps(conv(x))

Then define your conversion(s) to string as before:

scala> implicit def intToString(i: Int) = String.valueOf(i)
warning: there was one feature warning; re-run with -feature for details
intToString: (i: Int)String

scala> 100.toCharArray
res0: Array[Char] = Array(1, 0, 0)

scala> 100.reverse
res1: String = 001

scala> 100.length
res2: Int = 3
亚希 2024-08-10 21:45:18

我很困惑:你不能在任何类型上使用 .toString 从而避免隐式转换的需要吗?

I'm confused: can't you use .toString on any type anyway thus avoiding the need for implicit conversions?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文