2

Скажет, у меня есть следующие функции:неявного преобразования в Монадическом для понимания в Scala

case class ErrA(msg: String) 

case class ErrB(msg: String) 

def doA(): Either[ErrA, Int] = Right(2) 

def doB(): Either[ErrB, Int] = Right(3) 

ErrA и ErrB являются несвязанными типами и фактически не объявлен в одном файле за пределами этого примера. Они не могут быть легко наследованы от общего типа.

Я хотел бы представить новый тип, который будет представлять оба вида ошибок:

sealed trait ErrAOrB 

case class ErrAWrapper(e: ErrA) extends ErrAOrB 

case class ErrBWrapper(e: ErrB) extends ErrAOrB 

А затем написать следующую функцию с использованием для понимания:

def doAplusB(): Either[ErrAOrB, Int] = 
    for (a <- doA().right; 
     b <- doB().right) yield a + b 

Есть ли способ, чтобы получить компилятор неявно конвертирует те конкретные типы Either в общий тип Either[ErrAOrB, Int]?

Например:

implicit def wrapA[T](res: Either[ErrA, T]): Either[ErrAOrB, T] = res.left.map(ErrAWrapper(_)) 

implicit def wrapB[T](res: Either[ErrB, T]): Either[ErrAOrB, T] = res.left.map(ErrBWrapper(_)) 

Но это не работает, потому что неявное преобразование будет применяться только к окончательному выражению в для понимания, а затем компилятор должен связать его с doA и так типов ErrA и ErrAOrB не связаны друг с другом, что лучше всего сделать, чтобы сделать дженерики разумными - использовать Object, который несовместим с ожидаемым типом.

+1

Просто обратите внимание, что неявные представления не рекомендуются в Scala - вместо этого используйте неявный класс, поэтому он может быть smthng как 'doA(). Right.wrap'. Представьте, что люди читают ваш код и задаются вопросом, как «Либо [ErrA, Int]» стал «ErrAOrB»? Нет подсказки IDE, поэтому нет хорошего способа найти ваши «wrapA» implicits – dk14

ответ

2

Неявные представления не рекомендуются в Scala, как вы можете видеть из предупреждения о компиляторе при определении wrapA/wrapB.

Даже если вы возьмете ваши шансы определяя неявное вид на Either.RightProjection вместо Either - Только представьте, люди, читающие ваш код и задаваться вопросом о том, как же Either[ErrA, Int] стать Either[ErrAOrB, Int]? Нет IDE намека, так что нет хорошего способа, чтобы найти ваш Wrapa implicits

Таким образом, использовать неявный класс вместо:

implicit class WrapA[T](x: Either[ErrA, T]) { 
    def wrap = x.left.map(ErrAWrapper(_)): Either[ErrAOrB, T] 
} 

implicit class WrapB[T](x: Either[ErrB, T]) { 
    def wrap = x.left.map(ErrBWrapper(_)): Either[ErrAOrB, T] 
} 

scala> def doAplusB(): Either[ErrAOrB, Int] = 
    | for { 
    |  a <- doA().wrap.right 
    |  b <- doB().wrap.right 
    | } yield a + b 
doAplusB:()Either[ErrAOrB,Int] 

P.S. Вам не нужна монада для операции +, если ваши вычисления независимы (например, в вашем примере) - Аппликация достаточно. Взгляните на cats.data.Validated или scalaz.Validation, например.


Ответ кастрированный баран это возможно или не обмануть scalac:

implicit def wrapBB[T](res: Either.RightProjection[ErrB, T]): Either.RightProjection[ErrAOrB, T] = res.e.left.map(ErrBWrapper(_)).right 

implicit def wrapAA[T](res: Either.RightProjection[ErrA, T]): Either.RightProjection[ErrAOrB, T] = res.e.left.map(ErrAWrapper(_)).right 

def doAplusB(): Either[ErrAOrB, Int] = 
    for (a <- doA().right: Either.RightProjection[ErrAOrB, Int] ; 
     b <- doB().right: Either.RightProjection[ErrAOrB, Int]) yield a + b 

Но это требует Either.RightProjection типа приписывание, но если вы имели некоторое параллельное назначение (Аппликативный-стиль вместо для-понимания), я верьте, что вещь с ad-hoc супертипом может работать.

Или даже (с wrapB определена):

implicit def wrapAA[T] ... 
implicit def wrapBB[T] ... 

implicit def wrapB[T](res: Either[ErrB, T]): Either[ErrAOrB, T] = res.left.map(ErrBWrapper(_)) 

def doAplusB(): Either[ErrAOrB, Int] = 
    for (a <- doA().right: Either.RightProjection[ErrAOrB, Int]; 
     b <- doB().right) yield a + b 

Причина является экспансия:

doA().right.flatMap(a => doB().right.map(b => a + b)) 

flatMapRightProjection требует, чтобы быть возвращены, но map не делает.

+1

. Я полностью согласен с тем, что явное лучше, чем неявное, мне было просто любопытно, можно ли злоупотреблять имплицитами для достижения этого без каких-либо явных преобразований вообще. Фактические операции зависят друг от друга. –

Смежные вопросы