Возможно, мне не хватает чего-то очевидного, но я пытаюсь очистить какой-то шаблон в проекте, который использует Scalaz 7, и я не нахожу одну деталь головоломки, которая кажется довольно простой и, возможно, полезной.
Предположим, что мы имеем биекцию между двумя типами:
case class Foo(x: Int)
case class Bar(i: Int)
import scalaz._, Scalaz._, BijectionT._
val fb: Foo <@> Bar = bijection[Id, Id, Foo, Bar](
foo => Bar(foo.x),
bar => Foo(bar.i)
)
Теперь предположим, что нам нужно биекцию между List[Foo]
и List[Bar]
. Мы можем легко написать неявный класс, который предоставляет эту функциональность (на самом деле мы можем также заставить его работать для любого функтора):
implicit class BijectionLifter[A, B](val bij: A <@> B) extends AnyVal {
def liftInto[F[_]: Functor]: F[A] <@> F[B] = bijection[Id, Id, F[A], F[B]](
_ map bij.to,
_ map bij.from
)
}
Обратите внимание, что это простой перевод bimap
из Haskell Data.Bijection
. У бинауки Scalaz также есть метод с именем bimap
, но он имеет гораздо более занятый тип и, похоже, не делает того, что я хочу каким-либо очевидным образом.
Теперь мы можем просто написать следующее:
fb.liftInto[List]
И у нас есть требуемая биекция.
Мне не хватает некоторой абстракции, которая позволила бы мне написать это более чисто с функциями и примерами, уже предусмотренными для биекций в Scalaz 7?