Scala 三值运算符类型

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))

我实现了一个三元运算符,如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)) 
                        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