Scala相当于' forall a。设置 - >设置 - >设置一个'

时间:2014-09-02 15:26:34

标签: scala types

在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,并且交集(或其他)可以正常工作,因为交集不关心集合中的内容,只是它们'重新订购。也许我已经忘记了如何以非类似的方式做到这一点......

2 个答案:

答案 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)