如何使用Cats组合类型参数范围和函子?

时间:2018-07-24 06:53:26

标签: scala functional-programming functor type-parameter scala-cats

我遇到了很多用例,在我还使用类型参数范围的情况下,尝试结束编写FunctorApplicativeMonad等实例的情况。

例如...

import cats._

trait Preference[A] extends Order[A]

trait SocialWelfareFunction[-CC <: Iterable[P], +P <: Preference[_]]
  extends (CC => P)


object SocialWelfareFunction {

  def functor: Functor[({ type F[P <: Preference[_]] = SocialWelfareFunction[Iterable[P], P] })#F] = {
    ???
  }

...当我尝试对此进行编译时,会出现以下错误。

kinds of the type arguments ([P <: Playground.this.Preference[_]]Playground.this.SocialWelfareFunction[Iterable[P],P]) do not conform to the expected kinds of the type parameters (type F) in trait Monad.
[P <: Playground.this.Preference[_]]Playground.this.SocialWelfareFunction[Iterable[P],P]'s type parameters do not match type F's expected parameters:
type P's bounds <: Playground.this.Preference[_] are stricter than type _'s declared bounds >: Nothing <: Any

如何为我也使用类型参数的上下文编写FunctorApplicativeMonad等实例?可能吗有没有更合适的前进方向?

1 个答案:

答案 0 :(得分:0)

(Not a full solution, just a hint requested by OP to further clarify the question)

This example shows how to define Functor instances for type constructors that look almost as if they could be functors, but have several restrictions:

  1. Upper type bound <: UB on type parameter
  2. Require instances of typeclass TC

Here is a way around these two restrictions:

import scala.language.higherKinds

// Your favorite flavour of `Functor`,
// e.g. from `scalaz` or `cats`
trait Functor[F[_]] {
  def map[A, B](x: F[A], f: A => B): F[B]
}

// an upper bound
trait UB

// a typeclass
trait TC[X]


// A type constructor that is not a 
// functor, because it imposes upper bounds
// on the parameter, and because it requires
// a typeclass `TC` for `X`.
class Foo[X <: UB : TC] {
  def map[Y <: UB : TC](f: X => Y): Foo[Y] = ??? /* 
    some very clever implementation making use of `UB` and `TC`
  */
}

// A Functor that approximates `Foo[X]` for *arbitrary* `X`,
// without any restrictions.
abstract class WrappedFoo[X] { outer =>
  type Base <: UB
  val base: Foo[Base]

  protected val path: Base => X

  def map[Y](f: X => Y): WrappedFoo[Y] = new WrappedFoo[Y] {
    type Base = outer.Base
    val base = outer.base
    val path: Base => Y = outer.path andThen f
  }

  def unwrap[Y <: UB](
    implicit
    xIsY: X =:= Y,
    yTC: TC[Y]
  ): Foo[Y] = base.map(outer.path andThen xIsY)
}

// Functor instance for `WrappedFoo`
object WrappedFooFunctor extends Functor[WrappedFoo] {
  def map[A, B](x: WrappedFoo[A], f: A => B): WrappedFoo[B] = {
    x map f
  }
  def wrap[A <: UB](foo: Foo[A]): WrappedFoo[A] = new WrappedFoo[A] {
    type Base = A
    val base = foo
    val path = identity[A]
  }
}

object Example {

  // two "good" classes that conform to 
  // the upper bound and have instances
  // of the typeclass
  class Good1 extends UB
  class Good2(i: Int) extends UB

  implicit object Good1TC extends TC[Good1]
  implicit object Good2TC extends TC[Good2]

  val x1 = new Foo[Good1]       // start with "Foo[Good1]"
  val f: Good1 => Int = _.toString.length
  val g: Int => Good2 = i => new Good2(i)

  // Now we would like to go like this:
  // 
  //   Foo[Good1] ---f---> Foo[Int] ---g---> Foo[Good2]
  // 
  // The problem is: `Int` does not conform to `UB`,
  // and has no `TC` instance.

  // Solution:
  val x1w = WrappedFooFunctor.wrap(x1)
  val intermediate = x1w.map(f) // approximates "Foo[Int]"
  val x2w = intermediate.map(g) // wraps "Foo[Good2]"
  val x2 = x2w.unwrap           // only "Foo[Good2]"
}