猫:在没有类型别名的情况下为谓词实现逆变?
Cats: Implementing Contravariant for Predicates without a type alias?
说一个Predicate是一个函数A => Boolean,我想为谓词实现Cats的"Contravariant Functor"类型class的一个实例。
我还有一个隐式 class 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
我觉得这很烦人。所以我想知道,除了使用类型别名,我是否可以直接为从类型变量 A 到 Boolean 的 Function1 定义 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 的第一个参数应该保持 "hole",而第二个应该固定为布尔值?这可能吗?
查看 cats 的源代码,我在很多地方发现星号作为类型参数,但这对我也不起作用。
您可以使用 kind projector,它允许您使用星号 (*
) 来引用 "type hole"。
这样可以使用非常简单的语法来定义类型 * -> *
,即一元类型构造函数(采用单一类型来生成类型)。例如,采用某种类型 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
如果你不想使用额外的库,你可以在普通的 Scala 中使用 lambda 类型以一种更丑陋的方式来完成:
def predicateContra =
new Contravariant[({ type lambda[A] = Function1[A, Boolean] })#lambda] {
...
}
说一个Predicate是一个函数A => Boolean,我想为谓词实现Cats的"Contravariant Functor"类型class的一个实例。 我还有一个隐式 class 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
我觉得这很烦人。所以我想知道,除了使用类型别名,我是否可以直接为从类型变量 A 到 Boolean 的 Function1 定义 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 的第一个参数应该保持 "hole",而第二个应该固定为布尔值?这可能吗? 查看 cats 的源代码,我在很多地方发现星号作为类型参数,但这对我也不起作用。
您可以使用 kind projector,它允许您使用星号 (*
) 来引用 "type hole"。
这样可以使用非常简单的语法来定义类型 * -> *
,即一元类型构造函数(采用单一类型来生成类型)。例如,采用某种类型 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
如果你不想使用额外的库,你可以在普通的 Scala 中使用 lambda 类型以一种更丑陋的方式来完成:
def predicateContra =
new Contravariant[({ type lambda[A] = Function1[A, Boolean] })#lambda] {
...
}