在haskell中我可以写一个函数f where
f :: Set a -> Set a -> Set a
如果我选择两套,s1
&类型为s2
的{{1}},Set Int
f s1 s2
会产生Set Int
类型的内容。
然而,在scala中,我不能写这个,因为A是一些与之冲突的固定类型 长。
val x = Set(3L)
val y = Set(4L)
def foo[A](f: (Set[A], Set [A]) => Set [A]) = {
f(x,y)
}
我真正想要的是def foo[forall A. A] ...
。我怎么写这个?
编辑我的动机是从一个来源检索数据(x
& y
),以及从其他来源调用数据的方法。 x
& y
只是包含任何内容的一些集合,但已知是相同的类型
如果我有一些正确的多态函数,我可以通过x
& y
,并且交集(或其他)可以正常工作,因为交集不关心集合中的内容,只是它们'重新订购。也许我已经忘记了如何以非类似的方式做到这一点......
答案 0 :(得分:2)
在Scala和Haskell中,f
的类型将类似(直到同构):
f :: forall a. Set a -> Set a -> Set a
def f[A]: (Set[A], Set[A]) => Set[A]
Scala中的泛型类型参数的工作方式与Haskell中的类型变量完全相同。因此,我不确定为什么你说在Scala中这是不可能的 - 它不仅可能,而且看起来非常相似。您可以使用任意集合作为参数调用f
,就像您在Haskell中执行此操作一样:
f[Int](Set(1, 2), Set(3, 4))
当您想要将多态函数传递给另一个能够以任意类型使用它的函数时,差异就开始了。在Haskell中,它需要更高级别的多态性:
foo :: (forall a. Set a -> Set a -> Set a) -> Whatever
foo f = toWhatever $ f (makeSet [1, 2, 3]) (makeSet [4, 5, 6]) // you get the idea
Scala在其类型系统中没有与此直接等效。你需要做一个特殊的技巧来编码类型之间所需的关系。首先,定义一个额外的特征:
trait PolyFunction2[F[_], G[_], H[_]] {
def apply[A](f: F[A], g: G[A]): H[A]
}
然后你需要扩展这个特性来定义多态函数:
def f = new PolyFunction2[Set, Set, Set] {
def apply[A](f: Set[A], g: Set[A]): Set[A] = f ++ g
}
您需要使用此特征来定义类型参数:
def foo(f: PolyFunction2[Set, Set, Set]): (Set[Int], Set[String]) =
(f(Set(1, 2), Set(3, 4)), f(Set("a"), Set("b")))
scala> foo(f)
res1: (Set[Int], Set[String]) = (Set(1, 2, 3, 4),Set(a, b))
当然,这是一个临时实现,所以你最好使用Shapeless,因为它更通用。
答案 1 :(得分:1)
这是一个多态函数,使用shapeless
计算任意类型的两个集合的交集import shapeless._
import shapeless.poly._
object intersect extends Poly2 {
implicit def caseSet[A] = at[Set[A], Set[A]] { case (set1, set2) => set1 & set2 }
}
f(Set(3L, 4L), Set(4L, 5L)) // Set(4)
f(Set("foo", "bar", "baz"), Set("bar", "baz", "faz")) // Set("bar", "baz")
然后你可以定义一个方法,它采用任何可以在两个Set
s上运行的多态函数:
def foo[A](a: Set[A], b: Set[A], f: Poly2)(
implicit c: Case2[f.type, Set[A], Set[A]]
) = f(a, b)
f(Set(3L, 4L), Set(4L, 5L), intersect) // Set(4)
f(Set("foo", "bar", "baz"), Set("bar", "baz", "faz"), intersect) // Set("bar", "baz")
话虽如此,上面说的很整洁,但在你的情况下可能有些过分。在纯香草scala中,您可以改为
def foo[A](a: Set[A], b: Set[A])(f: Function2[Set[A], Set[A], Set[A]]) = f(a, b)
foo(Set(1L, 2L), Set(2L, 3L)){ case (s1, s2) => s1 & s2 } // Set(2)