简体   繁体   English

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

[英]Scala equivalent of 'forall a. Set a -> Set a -> Set a'

In haskell I can write a function f where 在haskell中我可以写一个函数f where

f :: Set a -> Set a -> Set a

and if I take two sets, s1 & s2 of type Set Int , and do f s1 s2 it would produce something of type Set Int . 如果我采用两个集合,类型为Set Int s1s2 ,并且执行f s1 s2 ,它将生成Set Int类型的东西。

In scala however, I cannot write this, because A is some fixed type, which conflicts with Long. 然而,在scala中,我不能写这个,因为A是一些固定类型,它与Long冲突。

val x = Set(3L)
val y = Set(4L)

def foo[A](f: (Set[A], Set [A]) => Set [A]) = {
  f(x,y)
}

What I really want though is def foo[forall A. A] ... . 我真正想要的是def foo[forall A. A] ... How can I write this? 我怎么写这个?

Edit The motivation is that I'm retrieving the data ( x & y ) from one source, and the method to call on them from another source. 编辑动机是我从一个源检索数据( xy ),以及从另一个源调用它们的方法。 x & y are just some sets containing anything, but known to be the same type. xy只是包含任何内容的一些集合,但已知是相同的类型。
If I have some properly polymorphic function, I could just pass the x & y in, and intersection (or whatever) would work fine because intersection doesn't care what's in the sets, only that they're ordered. 如果我有一些正确的多态函数,我可以只传递xy ,并且交集(或其他)可以正常工作,因为交集不关心集合中的内容,只是它们是有序的。 Perhaps I've gone forgotten how to do this in non haskell like ways... 也许我已经忘记了如何以非类似的方式做到这一点......

In Scala and in Haskell type of f will be similar (up to an isomorphism): 在Scala和Haskell中, f类型将类似(直到同构):

f :: forall a. Set a -> Set a -> Set a
def f[A]: (Set[A], Set[A]) => Set[A]

Generic type parameters in Scala work exactly in the same way as type variables in Haskell. Scala中的泛型类型参数的工作方式与Haskell中的类型变量完全相同。 So I'm not sure why you say that in Scala it is impossible - it is not only possible but it even looks very similar. 所以我不确定你为什么说在Scala中这是不可能的 - 它不仅可能,而且看起来非常相似。 You can call f with arbitrary sets as arguments, just like you'd do it in Haskell: 您可以使用任意集作为参数调用f ,就像在Haskell中执行它一样:

f[Int](Set(1, 2), Set(3, 4))

The difference starts when you want to pass a polymorphic function into another function which will be able to use it with arbitrary type. 当你想将多态函数传递给另一个能够以任意类型使用它的函数时,差异就开始了。 In Haskell it requires higher-rank polymorphism: 在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 does not have direct equivalent for this in its type system. Scala在其类型系统中没有与此直接等效。 You need to do a special trick to encode required relationship between types. 你需要做一个特殊的技巧来编码类型之间所需的关系。 First, define an additional trait: 首先,定义一个额外的特征:

trait PolyFunction2[F[_], G[_], H[_]] {
  def apply[A](f: F[A], g: G[A]): H[A]
}

Then you need to extend this trait to define polymorphic functions: 然后你需要扩展这个特性来定义多态函数:

def f = new PolyFunction2[Set, Set, Set] {
  def apply[A](f: Set[A], g: Set[A]): Set[A] = f ++ g
}

And you need to use this trait to define type parameters: 您需要使用此特征来定义类型参数:

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

Of course, this is an ad-hoc implementation, so you better use Shapeless as it is more general. 当然,这是一个临时实现,所以你最好使用Shapeless,因为它更通用。

Here's a polymorphic function that computes the intersection of two sets of any type, using 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")

Then you can define a method taking any polymorphic function that can operate on two Set s: 然后你可以定义一个方法,采用任何可以在两个Set上运行的多态函数:

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")

That being said, the above is neat, but probably overkill in your case. 话虽如此,上面说的很整洁,但在你的情况下可能有些过分。 In pure vanilla scala, you could instead do 在纯香草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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM