更新(2018年):我的祈祷在Dotty回答(输入Lambdas),所以下面的问答更加“Scalac”相关
Scala只是一个简单的例子:
scala> def f(x: Int) = x
f: (x: Int)Int
scala> (f _)(5)
res0: Int = 5
让我们通用:
scala> def f[T](x: T) = x
f: [T](x: T)T
scala> (f _)(5)
<console>:9: error: type mismatch;
found : Int(5)
required: Nothing
(f _)(5)
^
让我们看看Scala中的多态方法的eta扩展:
scala> f _
res2: Nothing => Nothing = <function1>
与Haskell比较:
Prelude> let f x = x
Prelude> f 5
5
Prelude> f "a"
"a"
Prelude> :t f
f :: t -> t
Haskell确实推断出了正确的类型 [T] => [T]
这里。
更现实的例子?
scala> identity _
res2: Nothing => Nothing = <function1>
更现实:
scala> def f[T](l: List[T]) = l.head
f: [T](l: List[T])T
scala> f _
res3: List[Nothing] => Nothing = <function1>
你不能为身份制作别名 - 必须编写自己的功能。像 [T,U](t: T, u: U) => t -> u
(make tuple)不可能用作值。更一般 - 如果你想传递一些依赖于泛型类型的lambda(例如:使用泛型函数,例如:创建列表,元组,以某种方式修改它们) - 你不能这样做。
那么,如何解决这个问题呢?任何解决方法,解决方案或推理?
附:我使用了术语多态lambda(而不是函数),因为函数只是名为lambda
只有方法在JVM / Scala上可以是通用的,而不是值。您可以创建一个实现某个接口的匿名实例(并为您要使用的每个类型都复制它):
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
}
或者使用无形' Poly
,它支持更复杂的类型案例。但是,这是一个限制,它需要解决。
我非常喜欢@Travis Brown的解决方案:
import shapeless._
scala> Poly(identity _)
res2: shapeless.PolyDefns.~>[shapeless.Id,shapeless.Id] = fresh$macro$1$2$@797aa352
-
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
-
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扩展到Shapeless2 Poly1
功能,(更多)真正的通用。但它不适用于多参数(即使是多类型参数),因此必须“实现” Poly2
同 implicit
+ at
方法(如@ som-snytt所建议的),类似于:
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)
附:我真的希望将它视为语言的一部分。
P∀scal 是一个编译器插件,它提供了更简洁的语法,可以使用泛型方法将多态值编码为对象。
身份函数作为值具有类型 ∀A. A => A
。要将其转换为Scala,请假设一个特征
trait ForAll[F[_]] {
def apply[A]: F[A]
}
然后身份函数有类型 ForAll[λ[A => A => A]]
,我在哪里使用 实物投影机 语法,或者没有kind-projector:
type IdFun[A] = A => A
type PolyId = ForAll[IdFun]
现在来了P∀scal语法糖:
val id = Λ[Α](a => a) : PolyId
或者等价的
val id = ν[PolyId](a => a)
(“ν”是希腊小写字母“Nu”,读作“new”)
这些只是昙花一现
new PolyId {
def apply[A] = a => a
}
P∀scal支持多种类型参数和任意种类的参数,但您需要对上述内容进行专门的修改 ForAll
每种变体的特征。
它似乎这样做你需要做一点类型提示来帮助Scala类型推理系统。
def id[T] : T => T = identity _
所以我想如果你试图将身份作为参数传递给函数调用,并且该参数的类型是通用的那么应该没有问题。