Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bundle imports in Scala

In my Scala project, almost all my files have these imports:

import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._

import spire.math._
import spire.implicits._

import com.wix.accord._
import com.wix.accord.dsl._

import codes.reactive.scalatime._

import better.files._

import java.time._

import scala.collection.mutable
...
...

What is the best way to DRY this in Scala? Can I specify all of them for my project (using some kind of sbt plugin?) or at the package level?

like image 879
pathikrit Avatar asked Sep 08 '16 22:09

pathikrit


Video Answer


1 Answers

I've seen a few approaches that kinda solve what you're looking for. Check out

Imports defined

https://github.com/mongodb/casbah/blob/master/casbah-core/src/main/scala/Implicits.scala

Small example of this approach:

object Imports extends Imports with commons.Imports with query.Imports with query.dsl.FluidQueryBarewordOps

object BaseImports extends BaseImports with commons.BaseImports with query.BaseImports

object TypeImports extends TypeImports with commons.TypeImports with query.TypeImports

trait Imports extends BaseImports with TypeImports with Implicits

@SuppressWarnings(Array("deprecation"))
trait BaseImports {
  // ...
  val WriteConcern = com.mongodb.casbah.WriteConcern
  // More here ...
}

trait TypeImports {
  // ...
  type WriteConcern = com.mongodb.WriteConcern
  // ... 
}

Imports used

https://github.com/mongodb/casbah/blob/master/casbah-core/src/main/scala/MongoClient.scala

When they use this import object, it unlocks all your type aliases for you. For example, WriteConcern

import com.mongodb.casbah.Imports._
// ...
def setWriteConcern(concern: WriteConcern): Unit = underlying.setWriteConcern(concern)

Essentially they wrap up all the imports into a common Import object, then just use import com.mycompany.Imports._

Doobie does something similar where most of the end-users just import doobie.imports._

https://github.com/tpolecat/doobie/blob/series/0.3.x/yax/core/src/main/scala/doobie/imports.scala

Again, a sample from this pattern:

object imports extends ToDoobieCatchSqlOps with ToDoobieCatchableOps {
  /**
   * Alias for `doobie.free.connection`.
   * @group Free Module Aliases
   */
  val FC   = doobie.free.connection

  /**
   * Alias for `doobie.free.statement`.
   * @group Free Module Aliases
   */
  val FS   = doobie.free.statement

  // More here ...
}

The main difference in this approach between the package object style is you get more control over what/when to import. I've used both patterns, usually a package object for common utility methods I'll need across an internal package. And for libraries, specifically the users of my code, I can attach certain implicit definitions to an import object like in doobie mentioned above that will unlock a DSL syntax for the user using a single import.

like image 199
Brian Pendleton Avatar answered Sep 23 '22 08:09

Brian Pendleton