Ответ 1
Ответ Alexey хороший, но я думаю, что мы можем перейти к более обобщаемому Scala рендерингу этой функции, если мы вставим его в несколько больший контекст.
Эдвин показывает использование StringOrInt
в функции valToString
,
valToString : (x : Bool) -> StringOrInt x -> String
valToString x val = case x of
True => cast val
False => val
В словах valToString
принимает первый аргумент Bool
, который фиксирует тип своего второго аргумента как либо Int
, так и String
и отображает последний как String
, подходящий для его типа.
Мы можем перевести это на Scala следующим образом:
sealed trait Bool
case object True extends Bool
case object False extends Bool
sealed trait StringOrInt[B, T] {
def apply(t: T): StringOrIntValue[T]
}
object StringOrInt {
implicit val trueInt: StringOrInt[True.type, Int] =
new StringOrInt[True.type, Int] {
def apply(t: Int) = I(t)
}
implicit val falseString: StringOrInt[False.type, String] =
new StringOrInt[False.type, String] {
def apply(t: String) = S(t)
}
}
sealed trait StringOrIntValue[T]
case class S(s: String) extends StringOrIntValue[String]
case class I(i: Int) extends StringOrIntValue[Int]
def valToString[T](x: Bool)(v: T)(implicit si: StringOrInt[x.type, T]): String =
si(v) match {
case S(s) => s
case I(i) => i.toString
}
Здесь мы используем множество Scala ограниченных зависимых типов функций для кодирования зависимых типов полного спектра Idris.
- Мы используем singleton-типы
True.type
иFalse.type
для перехода от уровня значения к типу уровня. - Мы кодируем функцию
StringOrInt
как класс типа, индексированный одноэлементнымиBool
типами, каждый случай функции Idris представлен отдельным неявным экземпляром. - Мы пишем
valToString
как зависимый от Scala метод, позволяющий использовать одноэлементный тип аргументаBool
x
для выбора неявногоStringOrInt
экземпляраsi
, который, в свою очередь, определяет тип параметрT
, который фиксирует тип второго аргументаv
. - Мы кодируем зависимое сопоставление шаблонов в Idris
valToString
с помощью выбранного экземпляраStringOrInt
, чтобы поднять аргументv
в Scala GADT, который позволяет совпадению шаблона Scala уточнять типv
на RHS случаев.
Выполняя это на Scala REPL,
scala> valToString(True)(23)
res0: String = 23
scala> valToString(False)("foo")
res1: String = foo
Множество обручей, которые нужно перепрыгнуть, и много случайной сложности, тем не менее, это можно сделать.