繁体   English   中英

Scala:如何将函子用于多类型参数特征

[英]Scala: How to use Functors for multi type parameter trait

我有和ADT基本上是Function1的包装:

case class Abstract[M[_], A, B](f:M[A] => M[B]) {
    def fn: M[A] => M[B] = { case x: M[A] => f(x) }
}

我想映射这些,所以我定义了一个Functor:

trait AbstractAPI[E] {
    type AbsO[T] = Abstract[List, E, T]
    // type AbsO[T] = Abstract[List, _, T] => does not work (?)

    implicit val abstractO: Functor[AbsO] = new Functor[AbsO] {
        def map[A, B](fa: AbsO[A])(f: A => B): AbsO[B] = {
            new Abstract(fa.fn andThen { x: List[A] => x.map{ y => f(y) } })
        }
    }
}

现在,要实际映射到Abstract,我需要AbstractAPI[Int] ,例如

case object IntAbstractAPI extends AbstractAPI[Int]

object A {
    import IntAbstractAPI._

    val f:List[Int] => List[String] = { case x: List[Int] => x.map{ _.toString.toLowerCase } }
    val hey = (new Abstract(f)).map{ x => x.toInt }
}

要么

object A extends AbstractAPI[Int] {

    val f:List[Int] => List[String] = { case x: List[Int] => x.map{ _.toString.toLowerCase } }

    // FINALLY!
    val res = (new Abstract(f)).map{ x => x.toInt }.map{ _.toFloat + 10f }
    // Abstract[List, Int, Float] = Abstract(<function1>)
}

但是,在这种模式下,我必须为每个可能的E定义案例对象。 这是我的问题:

  1. 这是使用函子的正确方法吗?
  2. 如何为每个可能的E自动创建case对象(或使编译器进行推断?)

编辑1:进一步说明:上面的实现有效,但是这一实现不起作用:

object A extends AbstractAPI {

    val f:List[Int] => List[String] = { case x: List[Int] => x.map{ _.toString.toLowerCase } }

    val res = (new Abstract(f)).map{ x => x.toInt }.map{ _.toFloat + 10f }
    // Abstract[List, Int, Float] = Abstract(<function1>)
}

给出编译错误:

value map is not a member of Abstract[List,Int,String]

我认为这是因为编译器无法为Abstract[List,Int,String]导出函子吗?

回答第二个问题,您可以尝试使用此隐式AbstractAPI[T]工厂:

implicit def abstractAPI[T]: AbstractAPI[T] = new AbstractAPI[T] {}

任何要求AbstractAPI[T]隐式证据都应该起作用,例如:

def f[T : AbstractAPI]: Unit = ()
f

您可以为不需要的类型参数派生一个函子。

import cats.Functor
import cats.syntax.functor._

我将Abstract第二类型参数重命名为X ,这将有所帮助

case class Abstract[M[_], X, A](f: M[X] => M[A]) // forget the fn bit for now

您不仅可以使用val创建类型类实例,还可以使用def创建类型类实例。 允许具有类型参数,也可以采用其他隐式(但仅隐式)参数。

type Abs1[X] = ({ type L[A] = Abstract[List, X, A] })

/*implicit*/ def abstract1[X]: Functor[Abs1[X]#L] = new Functor[Abs1[X]#L] {
override def map[A, B](fa: Abstract[List, X, A])(f: A => B): Abstract[List, X, B] =
      Abstract(mx => fa.f(mx).map(f))
}

如果mapList所需要的,则可以进一步概括具有Functor实例的任何M[_] 另外,将其放置在Abstract的伴随对象中,也可以找到它而无需其他导入/继承/等。

object Abstract {
  // Abstract.MX[M, X]#L can be replaced with Abstract[M, X, ?] if you use kind-projector
  type MX[M[_], X] = ({ type L[A] = Abstract[M, X, A] })

  implicit def genericFunctor[M[_]: Functor, X] = new Functor[MX[M, X]#L] {
    override def map[A, B](fa: Abstract[M, X, A])(f: A => B): Abstract[M, X, B] =
      Abstract(mx => fa.f(mx).map(f)) // the implementation is the same
  }
}

而且,如果您为M[_]导入了实例,也可以使用

assert {
  import cats.instances.list._ // get Functor[List]

  // map is automatically picked up from Functor[Abstract[List, Int, ?]]
  Abstract(identity[List[Int]])
    .map(Vector.range(0, _))
    .map(_.mkString(""))
    .f(List(1, 2, 3)) == List("0", "01", "012")
}

assert {
  import cats.instances.option._

  Abstract(identity[Option[Int]])
    .map(_ min 42)
    .map(i => Range(i, i + 3))
    .f(Some(11)) == Some(Range(11, 14))
}

您可以在那里尝试代码

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM