Я пытаюсь разработать неявный преобразователь типа Error | A в тип Either[Error, A] в Scala 3. Код преобразователя следующий:
object MyConversions:
given unionTypeToEither[Error, A]: Conversion[Error | A, Either[Error, A]] with
def apply(value: Error | A): Either[Error, A] = value match
case error: Error => Left(error)
case a: A => Right(a)
Конвертер должен работать со следующим кодом:
import in.rcard.raise4s.MyConversions.unionTypeToEither
val actual: String | Int = 42
val actualEither: Either[String, Int] = actual
Компилятор выдает мне следующую ошибку:
[error] 90 | val actualEither: Either[String, Int] = actual
[error] | ^^^^^^
[error] | Found: (actual : String | Int)
[error] | Required: Either[String, Int]
[error] |
[error] | longer explanation available when compiling with `-explain`
[error] one error found
Кто-нибудь может мне помочь?
A | B то же самое, что B | A, любой A то же самое, что A | Nothing - в то время как объединение 2 типов всегда однозначно, декомпозиция всегда неоднозначна. Вы не можете сделать это без, например. написание макроса с собственной логикой и дополнительными предположениями.
@MateuszKubuszok, ты был прав. Если вы опубликуете ответ, я приму его. Спасибо за подсказку.





Проблема возникает из-за разницы в семантике между Either[A, B] и A | B.
A | B то же самое, что B | A, любой A то же самое, что A | Nothing - хотя объединение 2 типов (с |) всегда однозначно, декомпозиция всегда неоднозначна. Вы не можете сделать это без, например. написание макроса с собственной логикой и дополнительными предположениями.
По моему опыту, если вы попытаетесь использовать вывод, например, разделите A | B, передав его во что-то вроде:
[X, Y](value: X | Y): Either[X, Y] = ...
на самом деле вы получите Either[A | B, Nothing] или что-то совершенно бесполезное.
Как человек, знакомый с вашими типами, вы, вероятно, предполагаете, что:
A и B имеют нижнюю верхнюю границу Any/AnyRef/AnyValA <:< B, ни B <:< A отношенийно, как правило, если все, что код знает о типах, это то, что они есть <: Any, тогда компилятору не с чем работать, и, вероятно, никто даже не пытался обработать вывод типа, чтобы заставить его работать разумным образом.
Предполагая, что у вас есть некий детерминированный способ решить, какой из компонентов идет первым, а какой последним (то есть: что будет слева, а что справа в A | B, поскольку это то же самое, что B | A), вам все равно придется написать это логику декомпозиции самостоятельно, возможно, написав макрос и признав, что некоторые случаи с его помощью невозможно обработать.
Для полноты картины я нашел, как выполнить преобразование между типом объединения E | A и типом Either[E, A]. Он использует класс типов TypeTest[T]:
def convertUnionToEither[E, A](
input: E | A
)(using aTest: TypeTest[Any, A], eTest: TypeTest[Any, E]): Either[E, A] =
input match
case a: A => Right(a)
case e: E => Left(e)
Проверка выполняется во время выполнения, а не во время компиляции. Более того, выполнение неявного преобразования невозможно, поскольку нам нужны два неявных экземпляра TypeTest.
Я думаю, что импорт должен быть
import in.rcard.raise4s.MyConversions.givenи вам также нужно импортироватьscala.language.implicitConversions- Более подробную информацию можно найти в документации: docs.scala-lang.org/scala3/book/ca-implicit-conversions.html - Кстати, это очень рискованное преобразование, также я бы посоветовал вместо этого добавить метод расширенияtoEither.