猫:在没有类型别名的情况下实现谓词逆变?

Ale*_*lex 1 functional-programming scala functor type-level-computation scala-cats

假设谓词是一个函数 A => Boolean,我想为谓词实现 Cats 的“逆变函子”类型类的实例。我还有一个隐式类 PredicateOps,它定义谓词的并集和相交运算符。

我已经能够使用类型别名使实例正常工作:

type Predicate[A] = A => Boolean

implicit val predicateContra = new Contravariant[Predicate] {
  override def contramap[A, B](fa: Predicate[A])(f: B => A): Predicate[B] =
    (b: B) => fa(f(b))
}
Run Code Online (Sandbox Code Playgroud)

但是当我这样做时,我必须将所有谓词函数强制为别名,如下所示:

val even: Predicate[Int] = (i: Int) => i % 2 == 0
Run Code Online (Sandbox Code Playgroud)

我觉得这很烦人。所以我想知道是否可以不使用类型别名,而是直接为 Function1 从类型变量 A 到 Boolean 定义 predicateContra,但我无法让它工作。以下两种想法都会给我带来编译器错误:

implicit val predicateContra = new Contravariant[Function1[_, Boolean]] {
// "Function1[_, Boolean] takes no type parameters, expected: one"

implicit def predicateContra[A] = new Contravariant[Function1[A, Boolean]] {
// "A => Boolean takes no type parameters, expected: one"
Run Code Online (Sandbox Code Playgroud)

如何告诉编译器 Function1 的第一个参数应保留为“洞”,而第二个参数应固定为布尔值?这可能吗?查看 cats 的源代码,我在很多地方发现星号作为类型参数,但这对我来说也不起作用。

slo*_*ouc 5

您可以使用kind 投影仪,它允许您用星号 ( ) 引用“类型孔” *

这使得定义 kind 类型的语法非常简单* -> *,即一元类型构造函数(采用单一类型来生成类型)。例如,采用某种类型A来生成该类型的类型Map[A, Int]可以简单地写为Map[*, Int]

那么你的代码就变成了:

val strToBool: String => Boolean = _.contains("1")
val intToStr: Int => String = _.toString

def predicateContra = 
  new Contravariant[Function1[*, Boolean]] {
    override def contramap[A, B](fa: A => Boolean)(f: B => A): B => Boolean = 
      (b: B) => fa(f(b))
  }

predicateContra.contramap(strToBool)(intToStr)(42) // false 
predicateContra.contramap(strToBool)(intToStr)(41) // true
Run Code Online (Sandbox Code Playgroud)

如果你不想使用额外的库,你可以通过使用 lambda 类型以一种有点丑陋的方式在普通 Scala 中完成:

def predicateContra =
  new Contravariant[({ type lambda[A] = Function1[A, Boolean] })#lambda] {
      ...
  }
Run Code Online (Sandbox Code Playgroud)