Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get implicit conversions to work inside collections?

Tags:

Say I have an implicit conversion:

implicit def aToB(a: A):B={
...
}

How can I get this implicit conversion to work on the elements of a List?

If I have:

val listOfA: List[A] ...

and I have a function that takes a List of B, is it possible to let Scala implicitly convert all of the elements from A's to B's?

Without implicit conversions, the conversion might look like:

lisftOfA.map(a => new B(a.someValue, a.anotherValue))

But I would love for this to happen like 'magic'... is that too much to ask.

like image 593
Jack Avatar asked Sep 16 '12 15:09

Jack


People also ask

How do you use implicit conversions?

An implicit conversion from type S to type T is defined by an implicit value which has function type S => T , or by an implicit method convertible to a value of that type. Implicit conversions are applied in two situations: If an expression e is of type S , and S does not conform to the expression's expected type T .

What are implicit conversions?

An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.

What is implicit conversion in Scala?

Implicit conversions in Scala are the set of methods that are apply when an object of wrong type is used. It allows the compiler to automatically convert of one type to another. Implicit conversions are applied in two conditions: First, if an expression of type A and S does not match to the expected expression type B.

Does C allow implicit conversion?

Implicit Type Conversion is also known as 'automatic type conversion'. It is done by the compiler on its own, without any external trigger from the user. It generally takes place when in an expression more than one data type is present.


2 Answers

Here are a few alternatives you might wish to consider:

1. Use a view bound

If it's possible to change the function that takes a List of Bs, this would be the simplest solution. Modify it to accept a List of things that can be converted to Bs. That is,

def yourFn(l: List[B]) = ...

would become

def yourFn[X <% B](l: List[X]) = ...

Then, you can just call the function with listOfA:

yourFn(listOfA)

2. Introduce a conversion method

This is similar to Rogach's first solution, except that the outer conversion is non-implicit:

def convert[B, A <% B](l: List[A]): List[B] = l map { a => a: B }

Then at your function's call-site, you would write

yourFn(convert(listOfA))

Like Rogach's second solution, this is bit safer than bringing in an implicit conversion.

3. Introduce an implicit conversion

This is equivalent to Rogach's first solution, but the notation is a bit nicer (IMO).

implicit def convert[B, A <% B](l: List[A]): List[B] = l map { a => a: B }

If this conversion is in scope at your call-site, you can just call your function with the listOfA:

yourFn(listOfA)

Parting Thoughts

It's interesting to consider how to solve this problem in a general way. What if I want to define my conversion method so that it can handle any type that implements the map method? i.e.,

def convert[B, A <% B, C[_]](c: C[A]): C[B] = c map { a => a: B }

This won't work, of course, since nothing in the signature expresses the constraint that C must implement map. As far as I know, expressing this constraint is fairly involved and cannot be done in a way that provides out-of-the-box support for any type that implements map. See Type-safe Scala sequence comprehensions.

like image 162
Aaron Novstrup Avatar answered Oct 19 '22 04:10

Aaron Novstrup


The following is a general solution, enabling implicit conversion of lists (List[A] => List[B]) if implicit conversion A=>B is available in scope:

scala> class A
defined class A

scala> class B
defined class B

scala> implicit def a2b(a:A) = new B
a2b: (a: A)B

scala> implicit def mapI[A,B](l: List[A])(implicit conv: A => B): List[B] = l.map(conv)
mapI: [A, B](l: List[A])(implicit conv: (A) => B)List[B]

scala> List(new A): List[B]
res0: List[B] = List(B@efa0bf4)

Is this what you need?

Also, since you already have that implicit conversion, you can just write:

listOfA.map(a2b) // List[B]

It would be a bit more verbose, but gives you a bit more explicit control of your code.

like image 42
Rogach Avatar answered Oct 19 '22 04:10

Rogach