关于haskell:如何在scala中获得通用(多态)lambda?

How to get generic (polymorphic) lambda in scala?

更新 (2018):我的祈祷在 Dotty (Type Lambdas) 中得到了回应,所以下面的 Q


在 JVM/Scala 上只有方法可以是泛型的,而不是值。您可以创建一个实现某些接口的匿名实例(并为您要使用的每个类型重复它):

1
2
3
4
5
6
7
trait ~>[A[_], B[_]] { //exists in scalaz
  def apply[T](a: A[T]): B[T]
}

val f = new (List ~> Id) {
  def apply[T](a: List[T]) = a.head
}

或者使用 shapeless\\' Poly,它支持更复杂的类型案例。但是,是的,这是一个限制,需要解决。


P?scal 是一个编译器插件,它提供了更简洁的语法,用于使用泛型方法将多态值编码为对象。

作为值的恒等函数具有类型?A. A => A。要将其转换为 Scala,请假设一个 trait

1
2
3
trait ForAll[F[_]] {
  def apply[A]: F[A]
}

然后恒等函数的类型为 ForAll[λ[A => A => A]],我使用 kind-projector 语法,或者,没有 kind-projector:

1
2
type IdFun[A] = A => A
type PolyId = ForAll[IdFun]

现在是 P?scal 语法糖:

1
val id = Λ[Α](a => a) : PolyId

或等效的

1
val id = ν[PolyId](a => a)

("ν"是希腊小写字母"Nu",读作"new")

这些实际上只是

的简写

1
2
3
new PolyId {
  def apply[A] = a => a
}

P?scal 支持多种类型参数和任意类型的参数,但您需要为每个变体在上述 ForAll 特征上进行专门的变体。


我真的很喜欢@Travis Brown 的解决方案:

1
2
3
4
import shapeless._

scala> Poly(identity _)
res2: shapeless.PolyDefns.~>[shapeless.Id,shapeless.Id] = fresh$macro$1$2$@797aa352

-

1
2
3
4
5
scala> def f[T](x: T) = x
f: [T](x: T)T

scala> Poly(f _)
res3: shapeless.PolyDefns.~>[shapeless.Id,shapeless.Id] = fresh$macro$2$2$@664ea816

-

1
2
3
4
5
6
7
8
9
10
11
scala> def f[T](l: List[T]) = l.head
f: [T](l: List[T])T

scala> val ff = Poly(f _)
ff: shapeless.PolyDefns.~>[List,shapeless.Id] = fresh$macro$3$2$@51254c50

scala> ff(List(1,2,3))
res5: shapeless.Id[Int] = 1

scala> ff(List("1","2","3"))
res6: shapeless.Id[String] = 1

Poly 构造函数(在某些情况下)会给你 eta-expansion 到 Shapeless2 Poly1 函数,这是(更多)真正通用的。但是它不适用于多参数(即使使用多类型参数),因此必须使用 implicit at 方法"实现" Poly2 (如@som-snytt 建议的那样),类似于:

1
2
3
4
5
6
7
8
9
object myF extends Poly2 {
  implicit def caseA[T, U] = at[T, U]{ (a, b) => a -> b}
}

scala> myF(1,2)
res15: (Int, Int) = (1,2)

scala> myF("a",2)
res16: (String, Int) = (a,2)

附言我真的很想把它看作是语言的一部分。


看起来你需要做一些类型提示来帮助 Scala 类型推断系统。

1
def id[T] : T => T = identity _

所以我想如果您尝试将身份作为参数传递给函数调用并且该参数的类型是通用的,那么应该没有问题。