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 的源代码,我在很多地方发现星号作为类型参数,但这对我来说也不起作用。
您可以使用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)
| 归档时间: |
|
| 查看次数: |
406 次 |
| 最近记录: |