How to get generic (polymorphic) lambda in scala?
更新 (2018):我的祈祷在 Dotty (Type Lambdas) 中得到了回应,所以下面的 Q
- @som-snytt Poly 似乎是一个很好的解决方法,所以我必须等待一个好的语法(才能真正看到 Poly 对象内的类型并具有 eta-expansion)。我的意思是 Shapeless 可以将 eta-expansion 定义为 Poly 以获得更好的语法。不管怎么说,还是要谢谢你。
-
@dk15 Scala 中的 Eta 扩展是方法和函数之间的一种笨拙的桥梁——它对 Shapelesss 多态函数值没有真正意义。
-
@Travis Brown - 我的意思是将方法扩展为 Poly 而不是 Function。所以我需要方便的桥接 Shapeless 的多功能而不是 scalas Function
-
@dk14 啊,明白了。 Poly(identity _) 等在某些情况下会起作用。
-
val a: (Int) => Int = f _; a(4) ?
-
@Travis Brown - 这就是我需要的 - 我曾经自己编写 Poly 函数(实现 Poly1 等) - 现在我的生活(在某些情况下)变得更轻松了。谢谢!
在 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,它支持更复杂的类型案例。但是,是的,这是一个限制,需要解决。
-
它实际上与 JVM 没有太大关系,因为它发生在编译时(也可以看到常规的 Function 解决方法)。我同意目前 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 特征上进行专门的变体。
-
这是非常好的!我提到过使用 kind-projector(类型级插件),我们(可能还没有尝试过)可以只写:type PolyId = ForAll[Lambda[A => A]] 甚至 val id = ν[ForAll[Lambda[A => A]]](a => a)。也许,您可以添加一些互操作性快捷方式(它可以在一个单独的项目中以使您的解决方案免受不必要的依赖)以及 val id = poly[A => A](a => a)
-
对于 poly[A => A](a => a),我们需要确定并硬编码要实现的特征(此处为 ForAll)。所以插件必须附带一个库,其中包含 ForAll[F[_]] 上的许多变体,如 ForAll2[F[_, _]]、ForAllH[F[_[_]]]、ForAllHA[F[_[_]], G[_]]、... 另一种选择是使用结构类型,根据一些人的说法是一个非首发。
-
我同意这种图书馆看起来"丑陋";但是,作为一个单独的(务实的)项目,为什么不呢?甚至可以说 shapeless 对 nat 有其限制,每个人都可以接受。此外,您可以使用宏生成这些类型(您可以为 Intellij IDEA 添加自定义扩展)。无论如何,对于一个平面参数,这个单线工作(Scala 2.12,鉴于 ForAll 已定义):val id = Λ[a](a => a) : ForAll[Lambda[a => a => a]],这真的很酷 :)
-
也(仅适用于在此处阅读评论的人)val toList = Λ[a](x => List(x)) : ForAll[Lambda[a => a => List[a]]]。 toList.apply(5) - scala 出于某种原因强迫我显式调用 apply
-
您至少需要一个显式 apply,因为它实际上是连续的两个 apply:toList.apply[Int].apply(5)。您也可以将其写为 toList[Int](5)。当 ForAll[F] 中的 F[_] 是函数类型时,这种特殊情况可能具有一些额外的语法便利,但这取决于库。
-
>"但是,作为一个单独的(务实的)项目,为什么不呢?"与什么分开?它不能与插件分开。
-
你可以在一个完全独立的项目中生成/硬编码 ForAll 的东西,因为它们根本不需要任何插件。快捷方式更复杂(也许这将是一个 hacky 解决方案),但它们仍然可以是一个单独的插件,将您的插件用作库(插件本质上是一个实现"插件"接口的 jar - 甚至种类 -投影机 KindRewriter 正式是一个公共类)
我真的很喜欢@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 中发生的变化太大了。你可能会看例如Idris 是一种从 Scala 中汲取灵感的语言,但对类型级编程有更多支持。
看起来你需要做一些类型提示来帮助 Scala 类型推断系统。
1
| def id [T ] : T => T = identity _ |
所以我想如果您尝试将身份作为参数传递给函数调用并且该参数的类型是通用的,那么应该没有问题。
- 它仍然不是一个值 - 无法以通用方式将其传递给另一个函数。简单地说,你在这里定义了一个方法——而不是一个函数
-
请参阅 stackoverflow.com/questions/2529184/
-
我的意思是 id _ 仍然是 Nothing => Nothing。我可以将其用作别名的解决方法,但不能用作通用解决方案。
-
看看:stackoverflow.com/questions/15264838/
-
好的,我的问题有点重复,但是我正在寻找该问题的任何通用解决方案 - 因为现在我们有宏和无形 - 也许有人创建了可以做到这一点的库。