Cats:为没有类型别名的谓词实现逆变?

问题描述 投票:0回答:1

说谓词是一个函数A =>布尔值,我想为谓词实现Cats的“ Contravariant Functor”类型类的实例。我还有一个隐式类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))
}

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

val even: Predicate[Int] = (i: Int) => i % 2 == 0

我觉得很烦。因此,我想知道是否可以直接为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"

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

scala functional-programming functor scala-cats type-level-computation
1个回答
1
投票

您可以使用kind projector,它允许您使用星号(*)来指代“类型孔”。

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

然后您的代码将成为:

val strToBool: String  => Boolean = _.size > 2
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)(12)  // false 
predicateContra.contramap(strToBool)(intToStr)(123) // true

如果您不想使用额外的库,则可以通过使用lambda在纯Scala中以某种更丑陋的方式进行:

def predicateContra =
  new Contravariant[({ type lambda[A] = Function1[A, Boolean] })#lambda] {
      ...
  }
© www.soinside.com 2019 - 2024. All rights reserved.