使用Unapply提取相同的类型类

whe*_*ies 9 scala scalaz scala-cats

我有以下情况,给出两种类型,MA并且MB,我希望能够证明它们不仅具有一个,Applicative而且它们都具有相同的底层形状.我尝试过以下操作:

type UnapplyM[TC[_[_]], MA, M0[_]] = Unapply[TC, MA]{ type M[X] = M0[X] }

implicit def thing[MA, MB, M[_]](implicit un: UnapplyM[Applicative,MA,M], un2: UnapplyM[Applicative,MB,M]) = ...
Run Code Online (Sandbox Code Playgroud)

但是继续遇到不同的含义(即这不起作用.)A类型参数Unapply和工作类型投影可以做类似的事情.

这有一种方法可以采用这两种类型并且能够证明它们实际上是由同一类型类实例支持的吗?

Tra*_*own 9

我首先要说的是一个完整的答案将是一个很长的故事,我在去年夏天的博客文章中讲了很大一部分,所以我将在这里略过一些细节并提供一个thingCats工作实施.

另外一个按语:这机器现在存在于Scalaz,以及一些"审查"的我的拉动请求将其添加有众多原因,我很高兴猫存在一个.:)

首先是一个完全不透明的类型,我甚至不会尝试在这里激励:

case class SingletonOf[T, U <: { type A; type M[_] }](
  widen: T { type A = U#A; type M[x] = U#M[x] }
)

object SingletonOf {
  implicit def mkSingletonOf[T <: { type A; type M[_] }](implicit
    t: T
  ): SingletonOf[T, t.type] = SingletonOf(t)
}
Run Code Online (Sandbox Code Playgroud)

接下来我们可以定义一个IsoFunctor,因为Cats目前似乎没有一个:

import cats.arrow.NaturalTransformation

trait IsoFunctor[F[_], G[_]] {
  def to: NaturalTransformation[F, G]
  def from: NaturalTransformation[G, F]
}

object IsoFunctor {
  implicit def isoNaturalRefl[F[_]]: IsoFunctor[F, F] = new IsoFunctor[F, F] {
    def to: NaturalTransformation[F, F] = NaturalTransformation.id[F]
    def from: NaturalTransformation[F, F] = to
  }
}
Run Code Online (Sandbox Code Playgroud)

我们可以使用比IsoFunctor我们即将做的更简单的东西,但它是一个很好的原则类型类,它是我在Scalaz中使用的,所以我会坚持使用它.

接下来是将Unapply两个Unapply实例捆绑在一起的新内容:

import cats.Unapply

trait UnapplyProduct[TC[_[_]], MA, MB] {
  type M[X]; type A; type B
  def TC: TC[M]
  type MA_ = MA
  def _1(ma: MA): M[A]
  def _2(mb: MB): M[B]
}

object UnapplyProduct {
  implicit def unapplyProduct[
    TC[_[_]], MA0, MB0,
    U1 <: { type A; type M[_] },
    U2 <: { type A; type M[_] }
  ](implicit
    sU1: SingletonOf[Unapply[TC, MA0], U1],
    sU2: SingletonOf[Unapply[TC, MB0], U2],
    iso: IsoFunctor[U1#M, U2#M]
  ): UnapplyProduct[TC, MA0, MB0] {
    type M[x] = U1#M[x]; type A = U1#A; type B = U2#A
  } = new UnapplyProduct[TC, MA0, MB0] {
    type M[x] = U1#M[x]; type A = U1#A; type B = U2#A
    def TC = sU1.widen.TC
    def _1(ma: MA0): M[A] = sU1.widen.subst(ma)
    def _2(mb: MB0): M[B] = iso.from(sU2.widen.subst(mb))
  }
}
Run Code Online (Sandbox Code Playgroud)

作为历史旁注,UnapplyProduct在Scalaz有任何有用的实例之前已存在四年.

现在我们可以这样写:

import cats.Applicative

def thing[MA, MB](ma: MA, mb: MB)(implicit
  un: UnapplyProduct[Applicative, MA, MB]
): Applicative[un.M] = un.TC
Run Code Online (Sandbox Code Playgroud)

然后:

scala> import cats.data.Xor
import cats.data.Xor

scala> thing(Xor.left[String, Int]("foo"), Xor.right[String, Char]('a'))
res0: cats.Applicative[[x]cats.data.Xor[String,x]] = cats.data.XorInstances$$anon$1@70ed21e4
Run Code Online (Sandbox Code Playgroud)

我们已经成功地讨论了编译器,以确定如何以Xor这样的方式分解这些类型,即它可以看到相关的Applicative实例(我们返回).