Scala equivalent to 'forall a. Install a & # 8594; Install a & # 8594; Set a '
In haskell, I can write a function f where
f :: Set a -> Set a -> Set a
and if I take two sets, s1
and s2
type Set Int
, and do f s1 s2
, it produces something like Set Int
.
In scala, however, I cannot write this because A is some fixed type that conflicts with Long.
val x = Set(3L)
val y = Set(4L)
def foo[A](f: (Set[A], Set [A]) => Set [A]) = {
f(x,y)
}
I really want to, but this def foo[forall A. A] ...
. How can I write this?
Edit. The motivation is that I extract the data ( x
and y
) from one source and method for a call from another source. x
and y
are just some sets containing something, but, as you know, of the same type.
If I have some valid polymorphic function, I can just pass x
& y
in, and the intersection (or whatever) will work fine, because the intersection doesn't care what's in the sets, just that they are ordered. Maybe I forgot how to do this in non haskell, how ways ...
source to share
In Scala and in Haskell, the type f
will be the same (up to isomorphism):
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 like type variables in Haskell. So I'm not sure why you are saying that this is not possible in Scala - not only is it possible, but it looks very similar. You can call f
with arbitrary sets as arguments, just like in Haskell:
f[Int](Set(1, 2), Set(3, 4))
The difference starts when you want to pass a polymorphic function to another function that can use it with an arbitrary type. Haskell requires higher rank polymorphism:
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 has no direct equivalent for this in its type system. You need to do a special trick to encode the required relationship between the types. First, define an additional characteristic:
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))
This is of course an ad-hoc implementation, so you are better off using Shapeless as it is more general.
source to share
Here's a polymorphic function that calculates 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 that accepts any polymorphic function that can run on two 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")
Having said that, the above is neat but probably overwhelming your case. In pure vanilla scala, you could do
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)
source to share