Во-первых: мы, вероятно, не можем повторно использовать else
, так как это ключевое слово, и использование обратных кавычек, чтобы заставить его рассматриваться как идентификатор, довольно уродливо, поэтому вместо этого я буду использовать otherwise
.
Вот попытка реализации.Сначала используйте шаблон pimp-my-library для добавления ifTrue
и ifFalse
к Boolean
.Они параметризованы для возвращаемого типа R
и принимают один параметр по имени, который должен оцениваться, если указанное условие реализуется.Но при этом мы должны разрешить вызов otherwise
.Таким образом, мы возвращаем новый объект с именем Otherwise0
(почему 0 объясняется позже), который сохраняет возможный промежуточный результат как Option[R]
.Это определяется, если текущее условие (ifTrue
или ifFalse
) реализовано, и в противном случае оно пустое.
class BooleanWrapper(b: Boolean) {
def ifTrue[R](f: => R) = new Otherwise0[R](if (b) Some(f) else None)
def ifFalse[R](f: => R) = new Otherwise0[R](if (b) None else Some(f))
}
implicit def extendBoolean(b: Boolean): BooleanWrapper = new BooleanWrapper(b)
На данный момент это работает и позволяет мне написать
someTest ifTrue {
println("OK")
}
Но без следующего предложения otherwise
, конечно, он не может вернуть значение типа R
.Итак, вот определение Otherwise0
:
class Otherwise0[R](intermediateResult: Option[R]) {
def otherwise[S >: R](f: => S) = intermediateResult.getOrElse(f)
def apply[S >: R](f: => S) = otherwise(f)
}
Он оценивает свой переданный именованный аргумент тогда и только тогда, когда промежуточный результат, который он получил из предшествующих ifTrue
или ifFalse
, не определен, что является именно темразыскиваетсяПараметризация типа [S >: R]
приводит к тому, что S
является наиболее конкретным общим супертипом фактического типа именованных параметров, например, r
в этом фрагменте имеет логический тип Fruit
:
class Fruit
class Apple extends Fruit
class Orange extends Fruit
val r = someTest ifTrue {
new Apple
} otherwise {
new Orange
}
Псевдоним apply()
позволяет вообще пропустить имя метода otherwise
для коротких кусков кода:
someTest.ifTrue(10).otherwise(3)
// equivalently:
someTest.ifTrue(10)(3)
Наконец, вот соответствующий сводник для Option
:
class OptionExt[A](option: Option[A]) {
def ifNone[R](f: => R) = new Otherwise1(option match {
case None => Some(f)
case Some(_) => None
}, option.get)
def ifSome[R](f: A => R) = new Otherwise0(option match {
case Some(value) => Some(f(value))
case None => None
})
}
implicit def extendOption[A](opt: Option[A]): OptionExt[A] = new OptionExt[A](opt)
class Otherwise1[R, A1](intermediateResult: Option[R], arg1: => A1) {
def otherwise[S >: R](f: A1 => S) = intermediateResult.getOrElse(f(arg1))
def apply[S >: R](f: A1 => S) = otherwise(f)
}
Обратите внимание, что теперь нам также нужен Otherwise1
, чтобы мы могли удобно передавать развернутое значение не только в аргумент функции ifSome
, но и в аргумент функции otherwise
после ifNone
.