Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can someone explain me implicit conversions in Scala?

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

like image 204
OscarRyz Avatar asked May 18 '10 22:05

OscarRyz


2 Answers

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

like image 91
GClaramunt Avatar answered Sep 23 '22 16:09

GClaramunt


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).)

like image 39
Rex Kerr Avatar answered Sep 24 '22 16:09

Rex Kerr