Need help with a design using implicit factories

I want to be able to write:

JavaList.of("X", "Y")

to get a mutable Java list. I’m okay with getting an ArrayList by default, but I’d like to keep the possibility of switching to LinkedList if needed.

Here’s my current attempt:

trait JavaListFactory[C[_]]:
   def from[A](seq: Seq[A]): C[A]

given ArrayList: JavaListFactory[util.ArrayList] with
   def from[A](seq: Seq[A]): util.ArrayList[A] = util.ArrayList(seq.asJava)

object LinkedList extends JavaListFactory[util.LinkedList]:
   def from[A](seq: Seq[A]): util.LinkedList[A] = util.LinkedList(seq.asJava)

object JavaList:
   def of[A, C[_] <: util.List[?]](values: A*)(using factory: JavaListFactory[C]): C[A] =
      factory.from(values)

and my questions:

  • I define my own factory because the standard ones seem too rich for my purpose (in particular, I don’t want to bother implementing a newBuilder method). Am I missing a simple one somewhere?

  • I can write JavaList.of("X", "Y") (and get an ArrayList) or JavaList.of("X", "Y")(using LinkedList) to get a LinkedList. However, I cannot switch from ArrayList to LinkedList with a simple import. Instead, I need a rather ugly given:

    given JavaListFactory[util.LinkedList] = LinkedList
    

    (which exposes the factory trait)

  • I also don’t like this util.List[?] in my signature.

Any suggestions for improvements?

Yeah, also I think that is wrong.
It should be C[x] <: util.List[x]

You could put each implicit into its own object, that way users must need to import one.

Like:

object JavaList:
  def of[A, C[x] <: util.List[x]](values: A*)(using factory: JavaListFactory[C]): C[A] =
    factory.from(values)

  trait JavaListFactory[C[_]]:
    def from[A](seq: Seq[A]): C[A]

  object arrayList:
    given JavaListFactory[util.ArrayList] with
      def from[A](seq: Seq[A]): util.ArrayList[A] =
        util.ArrayList(seq.asJava)

  object linkedList:
    given JavaListFactory[util.LinkedList] with
      def from[A](seq: Seq[A]): util.LinkedList[A] =
        util.LinkedList(seq.asJava)

And the usage would be something like:

import JavaList.arrayList.given

val javaList = JavaList.of("foo", "bar") // ArrayList
1 Like

I prefer this route so I can have ArrayList by default:

object ArrayList:
   given factory: JavaListFactory[util.ArrayList] with
      def from[A](seq: Seq[A]): util.ArrayList[A] = util.ArrayList(seq.asJava)

object LinkedList:
   given factory: JavaListFactory[util.LinkedList] with
      def from[A](seq: Seq[A]): util.LinkedList[A] = util.LinkedList(seq.asJava)

given JavaListFactory[util.ArrayList] = ArrayList.factory

This allows:

// ArrayList by default
JavaList.of("X", "Y")

// LinkedList explicitly
JavaList.of("X", "Y")(using LinkedList.factory)

// LinkedList by import
import LinkedList.factory
JavaList.of("X", "Y")

Thanks for the help.