Вопрос или проблема
Вот минимальный нерабочий пример того, что я пытаюсь сделать:
import io.github.iltotore.iron.*
import io.github.iltotore.iron.constraint.string.*
case class User(name: String)
type Username = String :| StartWith["@"]
def name2user(usern : Username) : User = User(usern)
Я хочу реализовать неявное преобразование так, чтобы:
val user : User = "@john" //компилируется
val user : User = "!john" //не компилируется
Вот что я попробовал до сих пор:
given name2user: Conversion[Username, User] = User(_)
val user1 : User = name2user("@john") //
val user2 : User = "@john" // не компилируется, потому что "@john" — это String
и
implicit def name2user[S <: String](n: S)(implicit
ev: S <:< Username
): User = User(n)
val user1: User = name2user2("@john") // не получается доказать, что String <:< Username
val user2: User = "@john" // не компилируется, потому что "@john" — это String
Еще раз, это минимальный пример, чтобы как можно проще проиллюстрировать мою цель интеграции уточнений Iron и неявных преобразований Scala 3.
Спасибо заранее за вашу помощь
Ответ или решение
Для реализации неявного преобразования в Scala 3 с использованием библиотеки Iron, необходимо учитывать, что хотелось бы иметь возможность преобразовывать строку, соответствующую определенному условию (в данном случае, строку, начинающуюся с "@" в Username
), в объект User
.
Приведенный вами код имеет некоторые недостатки, которые необходимо исправить. В частности, нужно правильно определить неявное преобразование, чтобы компилятор мог автоматически преобразовывать строку в тип Username
. Вот как это можно сделать:
import io.github.iltotore.iron.*
import io.github.iltotore.iron.constraint.string.*
case class User(name: String)
type Username = String :| StartWith["@"]
// Неявное преобразование строки в Username
given Conversion[String, Username] with {
def apply(s: String): Username =
(s: String :| StartWith["@"]) // Это преобразование будет выполнено, если строка валидна
}
// Неявное преобразование для создания User из Username
given Conversion[Username, User] with {
def apply(usern: Username): User = User(usern)
}
// Тестирование
val user1: User = "@john" // Компилируется и создается User
// val user2: User = "!john" // Не компилируется, так как "!john" не подходит под Username
Объяснение кода:
-
Определение типов и классов:
- Мы определяем
Username
какString
с условием, что он должен начинаться с "@".
- Мы определяем
-
Неявное преобразование из
String
вUsername
:- Используем
given
для определения неявного преобразования изString
вUsername
. При этом компилятор попытается выполнить проверку с помощью библиотеки Iron. Если строка не соответствует требованиям, то это приведет к ошибке компиляции.
- Используем
-
Неявное преобразование из
Username
вUser
:- Второе неявное преобразование меняет тип
Username
наUser
.
- Второе неявное преобразование меняет тип
Теперь, когда вы создаете объект User
с помощью строки, компилятор автоматически применяет указанные неявные преобразования, если строка соответствует проверке на тип Username
.
Заключение:
Используя неявные преобразования и систему типов Iron, вы можете достичь необходимой логики проверки типов в вашем коде. Это помогает сохранить безопасность типов и предотвратить ошибки на этапе компиляции, когда используется неправильное значение для имени пользователя.