Dylan - 1 month ago 23

Scala Question

Suppose I have a function-like type e.g.

`trait Parser[-Context, +Out]`

and I want to be able to combine multiple parsers such that the combined

`Context`

`Parser[Any, Int] + Parser[String, Long] = Parser[String, (Int, Long)]`

Parser[String, Int] + Parser[Any, Long] = Parser[String, (Int, Long)]

Parser[Option[Int], Foo] + Parser[Some[Int], Bar] = Parser[Some[Int], (Foo, Bar)]

Parser[String, Foo] + Parser[Int, Bar] = <should be a compile error>

To put the example in more concrete terms, suppose I have a function combiner like

`def zipFuncs[A, B1, B2](f1: A => B1, f2: A => B2): A => (B1, B2) = {`

a => (f1(a), f2(a))

}

and some functions like

`val f1 = { a: Any => 123 }`

val f2 = { a: String => 123 }

val f3 = { a: Option[Int] => 123 }

Now I can do

`> zipFuncs(f1, f2)`

res1: String => (Int, Int) = <function>

> zipFuncs(f1, f3)

res2: Option[Int] => (Int, Int) = <function>

> zipFuncs(f2, f3)

res3: Option[Int] with String => (Int, Int) = <function1>

But what I want is for

`zipFuncs(f2, f3)`

`String`

`Option[Int]`

`Option[Int]`

`String`

`res3`

I did create a typeclass:

`// this says type `T` is the most specific type between `T1` and `T2``

sealed trait MostSpecificType[T, T1, T2] extends (T => (T1, T2))

// implementation of `object MostSpecificType` omitted

def zipFuncs[A, A1, A2, B1, B2](f1: A1 => B1, f2: A2 => B2)(

implicit mst: MostSpecificType[A, A1, A2]

): A => (B1, B2) = { a: A =>

val (a1, a2) = mst(a)

f1(a1) -> f2(a2)

}

This accomplishes the goal described above, but with a really annoying problem. IntelliJ will highlight valid combinations as errors, inferring that the "most specific type (

`A`

`Nothing`

The highlighting issue is surely a bug in IntelliJ, and google searching seems to imply that various resets/cache wipes/etc should fix it (it didn't). Regardless of the blame, I'm hoping to find an alternate approach that both satisfies my original requirement, and doesn't confuse IntelliJ.

Answer

*If* you want this to work only when one of the types is a subtype of the other, then you can do this:

```
def Zip[A,X,Y](f: A => X, g: A => Y): A => (X,Y) = a => (f(a), g(a))
implicit class ZipOps[A,X](val f: A => X) extends AnyVal {
def zip[A0, Y](g: A0 => Y)(implicit ev: A0 <:< A): A0 => (X,Y) =
Zip({a: A0 => f(a)},g)
def zip[A0 >: A, Y](g: A0 => Y): A => (X,Y) =
Zip(f,g)
}
val f1: Any => Int = { a: Any => 123 }
val f2: String => Int = { a: String => 123 }
val f3: Option[Int] => Int = { a: Option[Int] => 123 }
val x1 = f1 zip f2 // works
val x1swap = f2 zip f1 // works
val x2 = f1 zip f3 // works
val x3 = f2 zip f3 // cannot prove that Option[Int] <:< String
val x3swap = f3 zip f2 // cannot prove that String <:< Option[Int]
```