[scala] Numerics and implicits

View: New views
13 Messages — Rating Filter:   Alert me  

[scala] Numerics and implicits

by Jesper Nordenberg :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Let's say you want to create a function that works on all numeric types.
We want nice syntax, so we define a wrapper class:

case class Num[T](v : T)(implicit n : Numeric[T]) {
   def +(v2 : T) = n.plus(v, v2)
   def *(v2 : T) = n.times(v, v2)
}

implicit def numericToNum[T : Numeric](v : T) = Num(v)

Now let's try it:

scala> def calc[T : Num](v1 : T, v2 : T) : T = v1 + v2
<console>:7: error: type mismatch;
  found   : T
  required: ?{val +: ?}
Note that implicit conversions are not applicable because they are
ambiguous:
  both method numericToNum in object $iw of type [T](v: T)(implicit
evidence$1: Numeric[T])Num[T]
  and method any2stringadd in object Predef of type (x:
Any)scala.runtime.StringAdd
  are possible conversion functions from T to ?{val +: ?}
        def calc[T : Num](v1 : T, v2 : T) : T = v1 + v2
                                                ^

Ouch, this is really bad. The problem has three components:

- The implicit "Any => StringAdd" conversion in Predef. Could probably
be changed to "String => StringAdd" without breaking too many apps.

- "T => Num[T]" is not considered more specific than "Any => StringAdd"
by the compiler.

- An implicit conversion in an inner scope is not prioritized over a
conversion in an outer scope.

What can be done to solve this problem?

/Jesper Nordenberg


[scala] Re: Numerics and implicits

by Jesper Nordenberg :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Jesper Nordenberg wrote:
> scala> def calc[T : Num](v1 : T, v2 : T) : T = v1 + v2

Should be: def calc[T : Numeric](v1 : T, v2 : T) : T = v1 + v2
but that doesn't affect the problem.

One solution is to unimport any2stringadd, "import
scala.Predef.{any2stringadd => _}" (thanks, Ricky), but that has obvious
drawbacks.

/Jesper Nordenberg


Re: [scala] Numerics and implicits

by Thomas Chust :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/10/14 Jesper Nordenberg <megagurka@...>:
> [...]
> Ouch, this is really bad. The problem has three components:
>
> - The implicit "Any => StringAdd" conversion in Predef. Could probably be
> changed to "String => StringAdd" without breaking too many apps.
> [...]

Hello,

if the implicit conversion to StringAdd is not applicable to any
value, it is pretty useless. As far as I can see it is mostly useful
to make the plus operator on strings and other values commutative;
without the conversion from Any to StringAdd something like

  val str = 42 + "foo"

would not be legal.

However, it should be possible to disable the conversion for
compilation units where it gets in your way using

  import Predef.{ any2stringadd => _ }

Ciao,
Thomas


--
When C++ is your hammer, every problem looks like your thumb.

[scala] Re: Numerics and implicits

by Jesper Nordenberg :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Thomas Chust wrote:
> if the implicit conversion to StringAdd is not applicable to any
> value, it is pretty useless.

I disagree. For example:

"1 + 1: " + 2 + ", 1 == 2: " + false

would still work fine.

 > As far as I can see it is mostly useful
> to make the plus operator on strings and other values commutative;

String concatenation isn't commutative (so the choice of the + operator
is not optimal, but that's another discussion).

/Jesper Nordenberg


Re: [scala] Re: Numerics and implicits

by Landei :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


Jesper Nordenberg wrote:
Thomas Chust wrote:
> if the implicit conversion to StringAdd is not applicable to any
> value, it is pretty useless.

I disagree. For example:

"1 + 1: " + 2 + ", 1 == 2: " + false

would still work fine.

 > As far as I can see it is mostly useful
> to make the plus operator on strings and other values commutative;

String concatenation isn't commutative (so the choice of the + operator
is not optimal, but that's another discussion).

/Jesper Nordenberg
+1

I'd rather write val s = "" + 42 + " answer" instead of having to fight to get my implicit conversions working because of that StringAdd-Predef-Thingy.

My 2c...

Daniel

Re: [scala] Re: Numerics and implicits

by Ismael Juma :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Wed, 2009-10-14 at 08:17 -0700, Landei wrote:
> +1
>
> I'd rather write val s = "" + 42 + " answer" instead of having to fight to
> get my implicit conversions working because of that StringAdd-Predef-Thingy.

Indeed. It would probably break quite a bit of code, but this is
probably the last change for something like that (if not too late
already).

Best,
Ismael


Re: [scala] Re: Numerics and implicits

by Paul Phillips-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Wed, Oct 14, 2009 at 05:02:16PM +0100, Ismael Juma wrote:
> Indeed. It would probably break quite a bit of code, but this is
> probably the last change for something like that (if not too late
> already).

I also would love to get rid of any2stringadd, although the general
problem of the degree of imposition imposed by Predef (and library
implicits in general -- as pointed out by retronym there is no real way
to work around those defined in a companion object) remains.

Still, one has to start somewhere and any implicit which operates on
"Any" had better come with a free trip to Bermuda.

--
Paul Phillips      | Beware of bugs in the above code; I have only
Apatheist          | proved it correct, not tried it.
Empiricist         |     -- Knuth
i'll ship a pulp   |----------* http://www.improving.org/paulp/ *----------

Re: [scala] Re: Numerics and implicits

by Thomas Chust :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/10/14 Jesper Nordenberg <megagurka@...>:

> Thomas Chust wrote:
>>
>> if the implicit conversion to StringAdd is not applicable to any
>> value, it is pretty useless.
>
> I disagree. For example:
>
> "1 + 1: " + 2 + ", 1 == 2: " + false
>
> would still work fine.
> [...]

Hello,

of course, but that's completely beside the point, since for that to
work, no implicit conversion is needed at all: The plus method on
strings can just accept any argument. I didn't say the conversion from
Any to StringAdd was necessary to convert something into a string, but
if the conversion to StringAdd was present at all and its source type
wasn't Any, then it could just as well be omitted completely -- it
would serve no purpose and be unnecessary ballast.

That said, I *do* like the fact that the string concatenation operator
in Scala is symmetric[1], accepting any type of argument on either
side and automatically converting it into a string, as long as at
least one of the arguments is already a string. Therefore I don't
think the any2stringadd implicit should be removed. Personally I would
have chosen a different name for the concatenation operator, for
example the binary "++" already used to mean concatenation for any
other type of sequence or maybe a binary "~", but that design decision
is most unlikely to change now.

Ciao,
Thomas


[1] I wrote "commutative" instead of "symmetric" in my previous
message by mistake :-(


--
When C++ is your hammer, every problem looks like your thumb.

Re: [scala] Re: Numerics and implicits

by Martin Odersky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I agree that String + is a wart but let's be realistic. Too much code
depends on it (in general and on the fact that it's symmetric). It
won't go. We just have to design around it.

Cheers

 -- Martin

On Wed, Oct 14, 2009 at 7:08 PM, Thomas Chust <chust@...> wrote:

> 2009/10/14 Jesper Nordenberg <megagurka@...>:
>> Thomas Chust wrote:
>>>
>>> if the implicit conversion to StringAdd is not applicable to any
>>> value, it is pretty useless.
>>
>> I disagree. For example:
>>
>> "1 + 1: " + 2 + ", 1 == 2: " + false
>>
>> would still work fine.
>> [...]
>
> Hello,
>
> of course, but that's completely beside the point, since for that to
> work, no implicit conversion is needed at all: The plus method on
> strings can just accept any argument. I didn't say the conversion from
> Any to StringAdd was necessary to convert something into a string, but
> if the conversion to StringAdd was present at all and its source type
> wasn't Any, then it could just as well be omitted completely -- it
> would serve no purpose and be unnecessary ballast.
>
> That said, I *do* like the fact that the string concatenation operator
> in Scala is symmetric[1], accepting any type of argument on either
> side and automatically converting it into a string, as long as at
> least one of the arguments is already a string. Therefore I don't
> think the any2stringadd implicit should be removed. Personally I would
> have chosen a different name for the concatenation operator, for
> example the binary "++" already used to mean concatenation for any
> other type of sequence or maybe a binary "~", but that design decision
> is most unlikely to change now.
>
> Ciao,
> Thomas
>
>
> [1] I wrote "commutative" instead of "symmetric" in my previous
> message by mistake :-(
>
>
> --
> When C++ is your hammer, every problem looks like your thumb.
>

[scala] Re: Numerics and implicits

by Eric Willigers :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Jesper Nordenberg wrote:
> implicit def numericToNum[T : Numeric](v : T) = Num(v)
> scala> def calc[T : Num](v1 : T, v2 : T) : T = v1 + v2

What do [T : Numeric] and [T : Num] mean?

Why does the compiler consider any2stringadd, given the second argument
to + is not a String?



Note that the following compiles:-

     implicit def numericToNum[T <: Numeric[T]]
         (v : T)(implicit n : Numeric[T]) = Num(v)

     def calc[T <: Num[T]](v1 : T, v2 : T) : T = v1 + v2





A small variation also compiles with the latest nightly build:-

     implicit def numericToNum[T <: Numeric[_]]
         (v : T)(implicit n : Numeric[T]) = Num(v)

     def calc[T <: Num[_]](v1 : T, v2 : T) : T = v1 + v2

although it failed with some September nightly builds:

error: type mismatch;
  found   : T
  required: _$2 where type _$2
     def calc[T <: Num[_]](v1 : T, v2 : T) : T = v1 + v2
                                                      ^


[scala] Re: Numerics and implicits

by Eric Willigers :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Eric Willigers wrote:
> A small variation also compiles with the latest nightly build:-

Correction: it also fails with the latest nightly build. Sorry for the spam.


Re: [scala] Re: Numerics and implicits

by Jorge Ortiz-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I can get this to compile with the latest nightly:

  implicit def numericOps[T : Numeric](x: T): Numeric[T]#Ops = implicitly[Numeric[T]].mkNumericOps(x)
  def foo[T <% Numeric[T]#Ops](x: T, y: T): T = x + y

If I try to leave out the result type on "foo" it complains with "error: recursive method foo needs result type", which is pretty mind-boggling.

--j

On Wed, Oct 14, 2009 at 4:40 PM, Eric Willigers <ewilligers@...> wrote:
Eric Willigers wrote:
A small variation also compiles with the latest nightly build:-

Correction: it also fails with the latest nightly build. Sorry for the spam.



Re: [scala] Re: Numerics and implicits

by Adriaan Moors-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



On Thu, Oct 15, 2009 at 1:29 AM, Eric Willigers <ewilligers@...> wrote:
What do [T : Numeric] and [T : Num] mean?
they're context bounds. [T : B] desugars to an implicit argument list (implicit evidence: B[T])
they generalise view bounds:  [T <% V] can be recovered as [T : ToV]
with a type alias  type ToV[X] = X => V[X]

adriaan