- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
finding implicits via typeclass
Thu, 2011-05-19, 21:33
please don't hesitate to correct any mistakes/misconceptions.
I'm still working on creating an improved Numeric typeclass. At this
point I have great performance (via specialization) and am now trying
to clean up the warts and syntactical clunkiness that are required to
use Numeric.
I'm currently running into an issue with creating implicit conversions
from primitives to generic numeric types. For instance:
// this works
def double[A:Numeric](a:A): A = {
val n = implicitly[Numeric[A]]
implicit def convertInt(x:Int) = n.fromInt(x)
a * 2
}
implicit def convertInt[A:Numeric](x:Int): A = {
val n = implicitly[Numeric[A]]
n.fromInt(x)
}
// so does this
def triple[A:Numeric](a:A): A = a * convertInt(3)
// but this explodes
def quadruple[A:Numeric](a:A): A = a * 4
There is a similar (but not identical) implicit created by Paul Phillips
that *does* work as intended, which looks something like this:
implicit def infixOps[A:Numeric](a:A) = new Ops(a)
// when infixOps is in scope, you can do things like this
def square[A:Numeric](a:A): A = a * a
Obviously these aren't the same use of implicits: in the first case the
compiler wants an A but finds an Int, and in the second case the
compiler needs to build something with a * method out of the A that it
found. But given that the compiler seems to be able to find the
infixOps method (and correctly pass along the implicit Numeric object
when calling it) I wonder why it can't find convertInt?
Thanks,
Fri, 2011-05-20, 14:07
#2
Re: finding implicits via typeclass
On Fri, May 20, 2011 at 08:51, Josh Suereth wrote:
> when the compiler sees x * 4, and x has type A and does *not* have a *
> method, then the compiler initiates an implicit search for:
> Function1[A, { def *(Int) : ? }] (or something akin to this). After finding
> all 'views' defined against A, it looks for one that has the * method
> defined.
> I think in your case, the compiler is looking for def *(x: Int) and *not*
> Function1[Int,A] to apply to 2. You could try:
> a * (4 : A) // Note: This forced the compiler to figure out how to change
> the 4 expression, not the a*4 expression.
I thought so too, but as far as I know -- and I admit I might well be
wrong--, the _parameters_ of a method are not looked into while
choosing implicit conversions. One of the reasons that lead to
ambiguous implicits. Moreover, this fails:
def quadruple[A:Numeric](a:A): A = { val n = implicitly[Numeric[A]];
n.times(a, 4) }
And there's no implicit conversion needed here except the one that is
provided. Well, at least as far as I can see. :-)
>
> - Josh
>
> On Thu, May 19, 2011 at 4:33 PM, Erik Osheim
> wrote:
>>
>> Disclaimer: I'm operating on the edge of my knowledge/ability, so
>> please don't hesitate to correct any mistakes/misconceptions.
>>
>> I'm still working on creating an improved Numeric typeclass. At this
>> point I have great performance (via specialization) and am now trying
>> to clean up the warts and syntactical clunkiness that are required to
>> use Numeric.
>>
>> I'm currently running into an issue with creating implicit conversions
>> from primitives to generic numeric types. For instance:
>>
>> // this works
>> def double[A:Numeric](a:A): A = {
>> val n = implicitly[Numeric[A]]
>> implicit def convertInt(x:Int) = n.fromInt(x)
>> a * 2
>> }
>>
>> implicit def convertInt[A:Numeric](x:Int): A = {
>> val n = implicitly[Numeric[A]]
>> n.fromInt(x)
>> }
>>
>> // so does this
>> def triple[A:Numeric](a:A): A = a * convertInt(3)
>>
>> // but this explodes
>> def quadruple[A:Numeric](a:A): A = a * 4
>>
>> There is a similar (but not identical) implicit created by Paul Phillips
>> that *does* work as intended, which looks something like this:
>>
>> implicit def infixOps[A:Numeric](a:A) = new Ops(a)
>>
>> // when infixOps is in scope, you can do things like this
>> def square[A:Numeric](a:A): A = a * a
>>
>> Obviously these aren't the same use of implicits: in the first case the
>> compiler wants an A but finds an Int, and in the second case the
>> compiler needs to build something with a * method out of the A that it
>> found. But given that the compiler seems to be able to find the
>> infixOps method (and correctly pass along the implicit Numeric object
>> when calling it) I wonder why it can't find convertInt?
>>
>> Thanks,
>>
Fri, 2011-05-20, 16:37
#3
Re: finding implicits via typeclass
On Fri, May 20, 2011 at 07:51:00AM -0400, Josh Suereth wrote:
> when the compiler sees x * 4, and x has type A and does *not* have a *
> method, then the compiler initiates an implicit search for:
>
> Function1[A, { def *(Int) : ? }] (or something akin to this). After finding
> all 'views' defined against A, it looks for one that has the * method
> defined.
>
> I think in your case, the compiler is looking for def *(x: Int) and *not*
> Function1[Int,A] to apply to 2. You could try:
>
> a * (4 : A) // Note: This forced the compiler to figure out how to change
> the 4 expression, not the a*4 expression.
I don't think this is what's happening... the error message I get when
compiling is:
[error] /home/eosheim/[redacted]: type mismatch
[error] found : Int(9)
[error] required: A
[error] numeric.plus(a, 9)
[error] ^
[error] one error found
I'm sorry if I confused the issue by using the infix * operator in my
example. To be clear, the error occurs even when using the
"numeric.plus" form rather than the infix operator "+". The definition
of this plus method occurs as follows:
trait Numeric[A] {
...
def plus(a:A, b:A):A
}
From what I can tell the compiler *knows* it needs a Function1[Int,A]
but it doesn't consider my implicit conversion. My best guess is that
maybe the implicit Numeric[A] parameter in the conversion is tripping
the compiler up?
Thanks,
Fri, 2011-05-20, 16:47
#4
Re: finding implicits via typeclass
On Fri, May 20, 2011 at 11:28:52AM -0400, Erik Osheim wrote:
> >From what I can tell the compiler *knows* it needs a Function1[Int,A]
> but it doesn't consider my implicit conversion. My best guess is that
> maybe the implicit Numeric[A] parameter in the conversion is tripping
> the compiler up?
I should have said that the reason I believe this is because things
like this do work:
def double[A:Numeric](a:A): A = {
val n = implicitly[Numeric[A]]
implicit def convertInt(x:Int) = n.fromInt(x)
n.times(a, 2)
}
This implies to me that when I can define my implicit conversion with a
specific Numeric object (n) then things are fine, but when I try to
"pick up" the current implicit Numeric parameter things go awry.
Fri, 2011-05-20, 17:27
#5
Re: finding implicits via typeclass
As I tried to state earlier, you have one too many implicit lookups happening.
the implicit view is finding a + method that requires an A. It will not perform a second implicit lookup to make the second argument match the expected Int IIRC.
- Josh
On Fri, May 20, 2011 at 11:33 AM, Erik Osheim <erik@plastic-idolatry.com> wrote:
the implicit view is finding a + method that requires an A. It will not perform a second implicit lookup to make the second argument match the expected Int IIRC.
- Josh
On Fri, May 20, 2011 at 11:33 AM, Erik Osheim <erik@plastic-idolatry.com> wrote:
On Fri, May 20, 2011 at 11:28:52AM -0400, Erik Osheim wrote:
> >From what I can tell the compiler *knows* it needs a Function1[Int,A]
> but it doesn't consider my implicit conversion. My best guess is that
> maybe the implicit Numeric[A] parameter in the conversion is tripping
> the compiler up?
I should have said that the reason I believe this is because things
like this do work:
def double[A:Numeric](a:A): A = {
val n = implicitly[Numeric[A]]
implicit def convertInt(x:Int) = n.fromInt(x)
n.times(a, 2)
}
This implies to me that when I can define my implicit conversion with a
specific Numeric object (n) then things are fine, but when I try to
"pick up" the current implicit Numeric parameter things go awry.
Fri, 2011-05-20, 18:47
#6
Re: finding implicits via typeclass
On Fri, May 20, 2011 at 13:18, Josh Suereth wrote:
> As I tried to state earlier, you have one too many implicit lookups
> happening.
> the implicit view is finding a + method that requires an A. It will not
> perform a second implicit lookup to make the second argument match the
> expected Int IIRC.
Sure it will:
scala> def double[A:Numeric](a:A): A = {
| val n = implicitly[Numeric[A]]
| import n._
| implicit def convertInt(x:Int) = n.fromInt(x)
| a * 2
| }
double: [A](a: A)(implicit evidence$1: Numeric[A])A
> - Josh
>
> On Fri, May 20, 2011 at 11:33 AM, Erik Osheim
> wrote:
>>
>> On Fri, May 20, 2011 at 11:28:52AM -0400, Erik Osheim wrote:
>> > >From what I can tell the compiler *knows* it needs a Function1[Int,A]
>> > but it doesn't consider my implicit conversion. My best guess is that
>> > maybe the implicit Numeric[A] parameter in the conversion is tripping
>> > the compiler up?
>>
>> I should have said that the reason I believe this is because things
>> like this do work:
>>
>> def double[A:Numeric](a:A): A = {
>> val n = implicitly[Numeric[A]]
>> implicit def convertInt(x:Int) = n.fromInt(x)
>> n.times(a, 2)
>> }
>>
>> This implies to me that when I can define my implicit conversion with a
>> specific Numeric object (n) then things are fine, but when I try to
>> "pick up" the current implicit Numeric parameter things go awry.
>>
Fri, 2011-05-20, 21:27
#7
Re: finding implicits via typeclass
On Fri, May 20, 2011 at 12:18:36PM -0400, Josh Suereth wrote:
> the implicit view is finding a + method that requires an A. It will not
> perform a second implicit lookup to make the second argument match the
> expected Int IIRC.
This is not what's happening (as Daniel demonstrated). But I think I
figured out what's going on.
Consider the following code:
import math.Numeric
def foo[A](a:A) (implicit n:Numeric[A]): A = n.zero
def bar[A](x:Int)(implicit n:Numeric[A]): A = n.zero
val a:Double = foo[Double](3.0) // a is 0.0
val b:Double = foo(3.0) // b is 0.0
val c:Double = bar[Double](6) // c is 0.0
val d:Double = bar(6) // EXPLODES! ambiguous implicits
The real problem seems to be that expressions are expected to return a
result independent of the LHS of an assignment, and thus generic type
parameters can't be pulled in that way.
Thus the compiler can't consider the required result type when figuring
out which implicit to use (and how to pass the implicit evidence along
correctly), while it *can* consider the argument types. This is why you
can create implicits like convertToDouble and infixOps but not
convertFromInt.
This is a real shame, although obviously I can work around it in some
cases by creating plus(A, Int), plus(A, Double), etc.
Function1[A, { def *(Int) : ? }] (or something akin to this). After finding all 'views' defined against A, it looks for one that has the * method defined.
I think in your case, the compiler is looking for def *(x: Int) and *not* Function1[Int,A] to apply to 2. You could try:
a * (4 : A) // Note: This forced the compiler to figure out how to change the 4 expression, not the a*4 expression.
- Josh
On Thu, May 19, 2011 at 4:33 PM, Erik Osheim <erik@plastic-idolatry.com> wrote: