Scala 三值运算符类型
我实现了一个三元运算符,如Java的Scala 三值运算符类型,scala,types,ternary-operator,Scala,Types,Ternary Operator,我实现了一个三元运算符,如Java的?:,将/替换为:,因为:不是有效的标识符: case class Ternary[T](val o: Option[T]) { def / (f: => T) = o getOrElse f } implicit def boolToTernary(cond: Boolean) = new { def ? [T](f: => T) = if(cond) Ternary(Some(f))
?:
,将/
替换为:
,因为:
不是有效的标识符:
case class Ternary[T](val o: Option[T]) {
def / (f: => T) = o getOrElse f
}
implicit def boolToTernary(cond: Boolean) = new {
def ? [T](f: => T) = if(cond) Ternary(Some(f))
else Ternary[T](None)
}
一般来说,它工作正常
scala> (1 > 2) ? "hi" / "abc"
res9: java.lang.String = abc
但在以下情况下会下降:
scala> (1 > 2) ? 5 / 6.0
<console>:33: error: type mismatch;
found : Double(6.0)
required: Int
(1 > 2) ? 5 / 6.0
^
scala>(1>2)?5 / 6.0
:33:错误:类型不匹配;
发现:双(6.0)
必填项:Int
(1 > 2) ? 5 / 6.0
^
如果(1>2)5 else 6.0能做到,我是否可以对类型进行任何调整,以使其像内置的
一样工作?我在谷歌上搜索了类似的解决方案,发现所有的实现都表现出相同的行为。这是我的版本,只是因为我很好奇。我不会真的用这个
我选择了低优先级的操作符<代码>^
将首先绑定,然后绑定|?
。我使用元组是协变的这一事实来推断结果的类型
case class TernClause[T](t: T) {
def ^[U](u: U) = (t, u)
}
case class Tern(b: Boolean) {
def |?[U](tuple: (U,U)) = if (b) tuple._1 else tuple._2
}
implicit def toTern(b: Boolean): Tern = Tern(b)
implicit def toTernClause[T](t: T): TernClause[T] = TernClause(t)
(1 > 2) |? "hi" ^ "abc"
// java.lang.String = abc
(1 > 2) |? 5 ^ 6.0
// AnyVal{def getClass(): java.lang.Class[_ >: Double with Int <: AnyVal]} = 6.0
此外,这在scalaz中可用,但他们将其命名为fold
:
import scalaz._
import Scalaz._
(1 > 2) fold (5, 6.0)
//res0: Double = 6.0
您可以做的一件事是将
/
的定义更改为:
def /[U >: T](f: => U) = o getOrElse f
它不像常规的if
(推断类型为Double
)那样工作-您可以获得AnyVal
,但这已经是一个改进,只需对代码进行非常小的修改
更新:我想我找到了一种(稍微复杂一点的)方法,使它的行为更像一个正常的if(例如,在本例中,推断出一个Double
)。请尝试以下代码:
implicit def boolToTernary(cond: Boolean) = new {
def ?[T](f: => T) = if (cond) Ternary(Some(f))
else Ternary[T](None)
}
case class Ternary[T](val o: Option[T]) {
def /[U, That](f: => U)(implicit conv: BiConverter[T, U, That]): That = o map conv.toThat1 getOrElse (conv toThat2 f)
}
class BiConverter[T, U, That](val toThat1: T => That, val toThat2: U => That)
trait LowPriorityBiConverterImplicits {
implicit def subtype[A, T <: A, U <: A]: BiConverter[T, U, A] = new BiConverter[T, U, A](identity[T], identity[U])
}
object BiConverter extends LowPriorityBiConverterImplicits {
implicit def identityConverter[T]: BiConverter[T, T, T] = new BiConverter[T, T, T](identity, identity)
implicit def firstAsSecond[T, U](implicit conv: T => U): BiConverter[T, U, U] = new BiConverter[T, U, U](conv, identity)
implicit def secondAsFirst[T, U](implicit conv: U => T): BiConverter[T, U, T] = new BiConverter[T, U, T](identity, conv)
}
我一直在使用@Jean-Philippe的解决方案,并添加了一些内容以允许对操作员进行链接。(好吧,我本可以保持原样,用括号括起来,但其中的乐趣何在?)可能有更好的方法来做到这一点,因此欢迎提出改进建议
implicit def boolToTernary(cond: Boolean) = new {
// operator name changed
def |? [T](f: => T) = if (cond) Ternary(Some(f)) else Ternary[T](None)
}
case class Ternary[T](o: Option[T]) {
def or [U, That](f: => U) (implicit conv: BiConverter[T, U, That]): That = o map conv.toThat1 getOrElse (conv toThat2 f)
// overload added
def or [U, That](t: Ternary[U])(implicit conv: BiConverter[T, U, That]): Ternary[That] = o match {
case x: Some[_] => Ternary(o map conv.toThat1)
case None => Ternary(t.o map conv.toThat2)
}
}
我更改了运算符名称:三元类中的运算符需要具有较低的优先级,但隐式定义中的运算符也需要具有较低的优先级,
具有除字母数字以外的最低优先级
我还添加了一个重载,这样我们就可以使用另一个三元子句
因此
1>2 |?4或4>6 |?8.0或10//看,没有括号!
//双精度=10.0
1 + 1 < 3 |?
4L或
4 > 6 |?
8或BigInt(10)
//scala.math.BigInt=4
酷还是什么?:)谁还需要
if/else
不错,但是推断的返回类型仍然是AnyVal
,而不是Double
,就像在常规if
中一样。对于这个更简单的版本,(1>2)?(BigInt(3),6)
返回Any=6
,而Jean-Philippe的版本返回BigInt=6
@Luigipling true,我认为额外的复杂性不值得,但我不得不承认,我没有意识到JPP的解决方案能够利用像BigInt.int2bigInt
这样的隐式转换+1.
implicit def boolToTernary(cond: Boolean) = new {
def ?[T](f: => T) = if (cond) Ternary(Some(f))
else Ternary[T](None)
}
case class Ternary[T](val o: Option[T]) {
def /[U, That](f: => U)(implicit conv: BiConverter[T, U, That]): That = o map conv.toThat1 getOrElse (conv toThat2 f)
}
class BiConverter[T, U, That](val toThat1: T => That, val toThat2: U => That)
trait LowPriorityBiConverterImplicits {
implicit def subtype[A, T <: A, U <: A]: BiConverter[T, U, A] = new BiConverter[T, U, A](identity[T], identity[U])
}
object BiConverter extends LowPriorityBiConverterImplicits {
implicit def identityConverter[T]: BiConverter[T, T, T] = new BiConverter[T, T, T](identity, identity)
implicit def firstAsSecond[T, U](implicit conv: T => U): BiConverter[T, U, U] = new BiConverter[T, U, U](conv, identity)
implicit def secondAsFirst[T, U](implicit conv: U => T): BiConverter[T, U, T] = new BiConverter[T, U, T](identity, conv)
}
abstract class Fruit
class Apple extends Fruit
class Banana extends Fruit
def main(args: Array[String]) {
val int = (1 > 2) ? 5 / 6 // Int is inferred
val fruit = (1 > 2) ? new Apple / new Banana // Fruit is inferred
val double1 = (1 > 2) ? 5 / 5.5 // Double is inferred
val double2 = (1 > 2) ? 5.5 / 5 // Double is inferred
}
implicit def boolToTernary(cond: Boolean) = new {
// operator name changed
def |? [T](f: => T) = if (cond) Ternary(Some(f)) else Ternary[T](None)
}
case class Ternary[T](o: Option[T]) {
def or [U, That](f: => U) (implicit conv: BiConverter[T, U, That]): That = o map conv.toThat1 getOrElse (conv toThat2 f)
// overload added
def or [U, That](t: Ternary[U])(implicit conv: BiConverter[T, U, That]): Ternary[That] = o match {
case x: Some[_] => Ternary(o map conv.toThat1)
case None => Ternary(t.o map conv.toThat2)
}
}
1 > 2 |? 4 or 4 > 6 |? 8.0 or 10 //look, no parentheses!
// Double = 10.0
1 + 1 < 3 |?
4L or
4 > 6 |?
8 or BigInt(10)
// scala.math.BigInt = 4