2016-11-08 9 views
1

Позвольте мне показать, что я ожидаю от условного оператора:Сжатый условный оператор в Scala

import scalaz._ 
import Scalaz._ 
import util.Random 

trait Card 

class BigCard extends Card 

class SmallCard extends Card 

object Main extends App { 
    def printCard(card: Card) {println(card.getClass.getSimpleName)} 

    // Line bellow works fine, but I would prefer less verbose conditional operator (like ?: from JavaScript). 
    (if (Random.nextBoolean) new BigCard else new SmallCard) |> printCard 

    // I thought ScalaZ's ?| was meant to be an alternative to if. But following statement fails to compile. 
    (Random.nextBoolean ? new BigCard | new SmallCard) |> printCard 
} 

Результат:

[error] xxx/Main.scala:15: type mismatch; 
[error] found : SmallCard 
[error] required: BigCard 
[error] (Random.nextBoolean ? new BigCard | new SmallCard) |> printCard 
[error]          ^
[error] one error found 
[error] (compile:compileIncremental) Compilation failed 

Есть ли альтернатива ScalaZ-х | оператор, который поддерживает подклассы (не уверен в отношении термина, или это расширение типа)?

Я ищу краткий условный оператор (поэтому вопрос о добавлении типов вручную не о чем идет, он будет намного длиннее и уродливее, чем if). Можно ли его легко добавить (как и без пользовательского макроса), или какая-то библиотека предоставляет такой оператор?

ответ

1

Да, это довольно легко (implicit class не может быть объявлена ​​на верхнем уровне, поставить это в object и импортировать его содержимое):

class Cond[A](x: Boolean, value: => A) { 
    def |[B >: A](other: => B) = if (x) value else other 
} 

implicit class CondOp(x: Boolean) { 
    def ?[A](y: => A) = new Cond(x, y) 
} 

Scalaz по-видимому, определяет def |(other: A) вместо этого.

Недостаток (и, предположительно, почему Scalaz не делает), что он также собирает для неродственных типов, так как в Scala любых два типа имеет общую супертип: Random.nextBoolean ? 1 | "". Кроме того, для x ? 1 | 1.0 вы не получите расширение до Double, как и для if; вместо этого он возвращает AnyVal.

+0

Спасибо, я не думал, что это будет так просто. Я проверю это. Я думал, что компилятор предупреждает о расширении Any/AnyVal/AnyRef. Я бы добавил call-by-name, поэтому он ведет себя скорее как классический if (не оценивая другую ветку). – monnef

+0

Работает отлично, спасибо :). Если кому-то нужна ленивая версия (или полный пример), посмотрите https://gist.github.com/mnn/3ef2e1bb67a93733a7d293c0fe7e10c2. – monnef

+0

«Я думал, что компилятор предупредил о расширении Any/AnyVal/AnyRef». Да, вы получите предупреждение в этом случае (и хороший вопрос об имени, я отредактирую ответ). –