- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Applying function to either side of Either
Tue, 2011-05-17, 18:43
Hi All,
Consider the following situation.
I have a trait and two classes for different kinds of numbers.
trait CNumber[C <: CNumber[C]] {
def negate: C
def add(c: C): C
}
case class CInt(val x: Int) extends AnyRef with CNumber[CInt] {
override def negate = CInt(-x)
override def add(c: CInt) = CInt(x + c.x)
}
case class CDouble(val x: Double) extends AnyRef with CNumber[CDouble]
{
override def negate = CDouble(-x)
override def add(c: CDouble) = CDouble(x + c.x)
}
Quest #1.
Given Either[C1, C2] object, I need to feed its contents to a function
that takes CNumber and returns some value.
object Apply1 {
def apply[C1 <: CNumber[C1], C2 <: CNumber[C2], T]
(c: Either[C1, C2],
func: (C forSome { type C <: CNumber[C] }) => T): T = {
c match {
case Left(left) => func(left)
case Right(right) => func(right)
}
}
}
println(Apply1(Left(CInt(1)), _.negate))
println(Apply1(Right(CDouble(2)), _.negate))
Output:
CInt(-1)
CDouble(-2.0)
So far so good.
Quest #2.
Given Either[C1, C2] object, I need to feeds its contents to a
function
that takes CNumber and returns CNumber of the same type,
then wrap the result back into Either.
object Apply2 {
def apply[C1 <: CNumber[C1], C2 <: CNumber[C2]]
(c: Either[C1, C2],
func: (C => C) forSome { type C <: CNumber[C] }): Either[C1, C2] =
{
c match {
case Left(left) => Left(func(left))
case Right(right) => Right(func(right))
}
}
}
This does not compile:
/home/beep/CNumber.scala:38: error: type mismatch;
found : left.type (with underlying type C1)
required: C where type C <: this.CNumber[C]
case Left(left) => Left(func(left))
^
/home/beep/CNumber.scala:39: error: type mismatch;
found : right.type (with underlying type C2)
required: C where type C <: this.CNumber[C]
case Right(right) => Right(func(right))
^
The main difference between this and previous code that I see
is that here I applied forSome to entire function type, while
in the first case I applied it only to the first parameter.
Quest #3.
Given two Either[C1, C2] objects, I need to feed their contents
to a function that takes two CNumber objects of the same type
and returns yet another CNumber of the same type, then wrap
the result back into Either.
object Apply3 {
def apply[C1 <: CNumber[C1], C2 <: CNumber[C2]]
(c1: Either[C1, C2],
c2: Either[C1, C2],
func: ((C, C) => C) forSome { type C <: CNumber[C] }): Either[C1,
C2] = {
c1 match {
case Left(left1) => c2 match {
case Left(left2) => Left(func(left1, left2))
case Right(_) => throw new AssertionError
}
case Right(right1) => c2 match {
case Left(_) => throw new AssertionError
case Right(right2) => Right(func(right1, right2))
}
}
}
}
This does not compile as well with the same error.
Is there any chance to get this code to work?
P.S. Scala 2.9.0
Tue, 2011-05-17, 22:27
#2
Re: Applying function to either side of Either
You are asking the compiler to trust you that the function returns the same type as it takes without making the types such that the function promises to do this.
You want to promise that
forall X f(CNumber[X]) has type CNumber[X]
and I don't know of a good way to do that with functions--maybe someone else can enlighten us as to a way to construct the appropriate type qualifier that survives conversion into a function.
What's wrong with (C => C) forSome { type C <: CNumber[C] } with respect to making such promise?
After all, (C forSome { type C <: CNumber[C] }) => T from the first case works as expected.
However, I noticed that if I change it to seemingly identical (C => T) forSome { type C <: CNumber[C] }, then the same compile error will appear.
Any ideas what's the difference between these two?
You could define it like so:
trait Q {
def f[A <: CNumber[A]](ca: CNumber[A]): A
}
object Apply2 {
def apply[C1 <: CNumber[C1], C2 <: CNumber[C2]] (
c: Either[C1, C2], q: Q
): Either[C1, C2] = c match {
case Left(left) => Left(q.f(left))
case Right(right) => Right(q.f(right))
}
}
which provides the appropriate promises, that is, that the _method_ is generic, not the class:
scala> Apply2( Left(CInt(4)), new Q { def f[A <: CNumber[A]](ca: CNumber[A]) = ca.negate } )
res5: Either[CInt,Nothing] = Left(CInt(-4))
Thanks, this indeed works fine.
It seems that in Scala functions cannot have type parameters,
so it makes sense to replace them with methods when the former are needed.
Yet I wonder if one can work around this restriction by using existential types.
Wed, 2011-05-18, 02:47
#3
Re: Applying function to either side of Either
On Tue, May 17, 2011 at 5:17 PM, Ilya Leoshkevich <mephi42@gmail.com> wrote:
You are asking the compiler to trust you that the function returns the same type as it takes without making the types such that the function promises to do this.
You want to promise that
forall X f(CNumber[X]) has type CNumber[X]
and I don't know of a good way to do that with functions--maybe someone else can enlighten us as to a way to construct the appropriate type qualifier that survives conversion into a function.
What's wrong with (C => C) forSome { type C <: CNumber[C] } with respect to making such promise?
After all, (C forSome { type C <: CNumber[C] }) => T from the first case works as expected.
However, I noticed that if I change it to seemingly identical (C => T) forSome { type C <: CNumber[C] }, then the same compile error will appear.
Any ideas what's the difference between these two?
In the first case, T is a parameter of apply. The appropriate T is picked when you call apply.
In the second case, you want a function that does not pick _some_ C such that it maps C => C, but rather that for all C such that C <: CNumber[C], the function will map that C to the same type C.
Unfortunately, the type annotations you tried to use only promise that there is _some_ C, not that _every_ C works that way.
--Rex
You want to promise that
forall X f(CNumber[X]) has type CNumber[X]
and I don't know of a good way to do that with functions--maybe someone else can enlighten us as to a way to construct the appropriate type qualifier that survives conversion into a function.
You could define it like so:
trait Q {
def f[A <: CNumber[A]](ca: CNumber[A]): A
}
object Apply2 {
def apply[C1 <: CNumber[C1], C2 <: CNumber[C2]] (
c: Either[C1, C2], q: Q
): Either[C1, C2] = c match {
case Left(left) => Left(q.f(left))
case Right(right) => Right(q.f(right))
}
}
which provides the appropriate promises, that is, that the _method_ is generic, not the class:
scala> Apply2( Left(CInt(4)), new Q { def f[A <: CNumber[A]](ca: CNumber[A]) = ca.negate } )
res5: Either[CInt,Nothing] = Left(CInt(-4))
Quest #3 has a similar problem and solution.
--Rex
On Tue, May 17, 2011 at 1:43 PM, Ilya Leoshkevich <mephi42@gmail.com> wrote: