Я пытаюсь создать бедную версию реализации уточненных типов с простой проверкой содержимого строки и проверкой возвращаемого типа с помощью Implicit
свидетельства другой функции. Кажется, что тип был стерт после того, как он был возвращен из функции check
. Может ли кто-нибудь предложить мне трюк scala, чтобы сделать это эффективно?
sealed trait Validate
sealed trait Pass extends Validate
sealed trait Fail extends Validate
type SUCCESS = Pass
case class Validator[State <: Validate: TypeTag](s: String = "") {
def check = {
if (s == "awesome") {
Validator[Pass](s)
} else {
Validator[Fail](s)
}
}
def pass: Validator[Pass] = Validator[Pass](s)
def fail: Validator[Fail] = Validator[Fail](s)
def validate(implicit ev: State =:= SUCCESS): String = s
def testResult = typeOf[State] =:= typeOf[SUCCESS]
}
Validator("awesome").check.testResult // true
Validator("awesome").check.validate // Cannot prove that _1 =:= MdocApp.this.SUCCESS.
Если я правильно понял вашу цель, вы хотели бы заменить реализацию с использованием scala-reflect
TypeTag
(testResult
) на реализацию с использованием статических типов и имплицитов (validate
).
Тип возвращаемого значения check
— это просто Validator[Validate]
, а не Validator[Pass]
или Validator[Fail]
.
Типы разрешаются во время компиляции, поэтому компилятор должен знать, является ли строка "awesome"
во время компиляции. Например, вы можете поднять s
, чтобы ввести уровень.
Строка с типом String
слишком грубая, вместо этого вы можете использовать точные одноэлементные подтипы. Вы можете ввести класс типов для "сопоставления шаблонов" во время компиляции.
// type class
trait Check[S <: String] {
type State <: Validate
}
trait LowPriorityCheck {
type Aux[S <: String, State0 <: Validate] = Check[S] {type State = State0 }
implicit def default[S <: String]: Aux[S, Fail] = null
}
object Check extends LowPriorityCheck {
implicit val pass: Aux["awesome", Pass] = null
}
case class Validator[S <: String with Singleton, State <: Validate](s: S) {
def check(implicit check: Check[S]): Validator[S, check.State] =
Validator[S, check.State](s)
def validate(implicit ev: State =:= SUCCESS): String = s
}
Validator("awesome").check.validate // awesome
Validator("abc").check.validate // Cannot prove that Fail =:= SUCCESS
Если вы предпочитаете сохранить обе реализации, вы можете восстановить TypeTag
границы контекста. Тогда вам придется сделать метод check
универсальным. Лучше сделать State
параметром типа класса типа, а не членом типа (иначе будет ошибка «невозможно создать TypeTag
ссылку на тип LowPriorityCheck.<refinement>.State
, локальный по отношению к reifee: вместо этого используйте WeakTypeTag
», но используя WeakTypeTag
/weakTypeOf
вместо TypeTag
/ typeOf
слишком прекрасно, weakTypeOf[State] =:= typeOf[SUCCESS]
никогда не бывает правдой).
trait Check[S <: String, State <: Validate]
trait LowPriorityCheck {
implicit def default[S <: String]: Check[S, Fail] = null
}
object Check extends LowPriorityCheck {
implicit val pass: Check["awesome", Pass] = null
}
case class Validator[S <: String with Singleton, State <: Validate: TypeTag](s: S) {
def check[St <: Validate : TypeTag](implicit
check: Check[S, St]
): Validator[S, St] = Validator[S, St](s)
//def check[St <: Validate : Check[S, *] : TypeTag]: Validator[S, St] = Validator[S, St](s)
def testResult = typeOf[State] =:= typeOf[SUCCESS]
def validate(implicit ev: State =:= SUCCESS): String = s
}
Спасибо за ответ. Как вы думаете, возможно ли сделать более динамическую проверку вместо того, чтобы сравнивать с константой «круто» (например, string.length> 5), используя этот подход? Я предполагаю, что нет, поскольку компилятор не сможет сделать это во время компиляции.