views:

246

answers:

2

And more specifically how does the BigInt works for convert int to BigInt?

In the source code it reads:

...
implicit def int2bigInt(i: Int): BigInt = apply(i)
...

How is this code invoked?

I can understand how this other sample: "Date literals" works.

In.

val christmas = 24 Dec 2010  

Defined by:

implicit def dateLiterals(date: Int) = new {
  import java.util.Date  
  def Dec(year: Int) = new Date(year, 11, date)
}

When int get's passed the message Dec with an int as parameter, the system looks for another method that can handle the request, in this case Dec(year:Int)

Q1. Am I right in my understanding of Date literals?

Q2. How does it apply to BigInt?

Thanks

+8  A: 

When a the provided type doesn't match the expected type, the Scala compiler looks for any method in scope marked implicit that takes the provided type as parameter and returns the expected type as a result. If found, it inserts the call to the method in between. In the BigInt case, say you have a method

doSomethingWithBigInt(d:BigInt)=....

And you call it with an integer:

doSomethingWithBigInt(10)

As the types don't match, the Scala compiler will generate:

doSomethingWithBigInt(int2bigInt(10))

Assuming the implicit int2bigInt is in scope

GClaramunt
To clarify, it uses an implicit *if it will make code that wouldn't otherwise compile do so*, not just if there's an implicit that accepts the found type as its argument.
Randall Schulz
The scope thingy is a red herring. Scala will search for such implicits in the companion object of the original class, and in the companion object of the destination class if that destination class has been inferred. In this case, since the destination class is explicitly BigInt, then BigInt's object companion will be searched for implicits.
Daniel
I didn't want to go into details about the scope :)
GClaramunt
@Daniel: In this case the scope is, as you say, not a problem. But should the OP wish to go and implement some implicit conversions of their own I'd have to agree with @GClaramunt that it's worth mentioning briefly at least.
pdbartlett
+5  A: 

The point of implicit stuff is to fill in boring boilerplate stuff when there is clearly only one right way to do it.

In the case of implicit parameters the compiler inserts a parameter from context that must be what you were thinking of. For example,

case class TaxRate(rate: BigDecimal) { }
implicit var sales_tax = TaxRate(0.075)
def withTax(price: BigDecimal)(implicit tax: TaxRate) = price*(tax.rate+1)

scala> withTax(15.00)
res0: scala.math.BigDecimal = 16.1250

Since we've marked the tax rate as an implicit parameter, and provided an implicit variable that can be filled in when needed, we don't need to specify the tax rate. The compiler automatically fills in withTax(15.00)(sales_tax)

In the case of implicit conversions, the compiler looks for a method that can take a type that it has and convert it to the type that is needed. This conversion cannot be chained under normal circumstances, so you have to get to what you need in one step.

There are two cases where implicit conversions are likely to come into play. One is in the parameter of a method call--if the type is wrong, but it can be converted to the right type (in exactly one way), then the compiler will convert for you. The other is in the presence of a method call--if the type actually used doesn't have the method available, but you could convert it to a type that does have that method, then the conversion will take place and then the method will be called.

Let's look at an example of each.

implicit def float2taxrate(f: Float) = TaxRate(BigDecimal(f))
scala> withTax(15.00)(0.15f)
res1: scala.math.BigDecimal = 17.250000089406967200

Here, we call an explicit tax rate of 0.15f. That doesn't match the parameter, which must be of type TaxRate, but the compiler sees that we can turn floats into tax rates using the implicit float2taxrate. So it does it for us, calling withTax(15.00)(float2taxrate(0.15f))

Now the other example.

class Currency(bd: BigDecimal) {
  def rounded = bd.setScale(2,BigDecimal.RoundingMode.HALF_EVEN)
}
implicit def bigdec2currency(bd: BigDecimal) = new Currency(bd)
scala> withTax(15.00)(0.15f).rounded
res66: scala.math.BigDecimal = 17.25

BigDecimal doesn't have a rounded method, so withTax(15.00)(0.15f) shouldn't be able to call one (as it returns a BigDecimal). But we've defined a Currency that does have a rounded method, and a conversion to Currency, so the implicit conversion fills in all the details: bigdec2currency(withTax(15.00)(0.15f)).rounded.

In the case of the conversion from Int to BigInt, the compiler will use it when, for example, it tries to add 7 + BigInt(5). This isn't going to work normally--7 is an Int and Int doesn't know how to add itself to BigInt. But BigInt has a method + that can add itself to another BigInt. And the compiler sees that if only it could convert 7 to a BigInt, it could use that method. The implicit conversion allows that conversion, so it translates 7 + BigInt(5) into int2bigInt(7)+BigInt(5).

(Note: int2bigInt is defined inside BigInt, so to use it you have to import BigInt._. And it in turn defers to the apply(i: Int) method of the BigInt object, which is what lets you write BigInt(5) and have it work (rather than having to pass a string as with BigInteger in Java).)

Rex Kerr
Thanks! That's more detailed than my explanation.
GClaramunt
Yours was likely sufficient, but I decided to leave a lengthier reference/tutorial for those who are less familiar with Scala.
Rex Kerr