d money
d money

Reputation: 57

Aliasing Scala package objects

I'm developing a library that depends on another. The dependency has a package object that I'd like to alias into my own package domain, to 'hide' the underlying library from the users of the one I'm developing, for potential later reimplementation of that library. I've tried a couple things, including

object functions {
  def identity(a: Any): Any = a
  def toUpper(s: String): String = s.toUpperCase
}
object renamedfunctions {
  import functions._
}

This compiles but import renamedfunctions._ brings nothing into scope. I've also tried extending the backing object, but scala objects are un-extendable. Does anyone know of a way to accomplish what I'm trying to do without forking the underlying library?

Upvotes: 4

Views: 9200

Answers (3)

som-snytt
som-snytt

Reputation: 39577

Unfortunately, the commented-out code crashes the compiler:

package object p { def f = 42 }

package q {
  object `package` { def f = p.f }
}
/*
package object q {
  val `package` = p.`package`
}
*/

package client {
  import q.`package`._
  object Test extends App {
    println(f)
  }
}

That would make clients not break when you migrated to implementations in a package object.

Upvotes: 2

Michael Zajac
Michael Zajac

Reputation: 55569

It is not possible to do this with Scala packages, in general. Usually, you would only alias a package locally within a file:

import scala.{ math => physics }

scala> physics.min(1, 2)
res6: Int = 1

But this doesn't do what you ask. Packages themselves aren't values or types, so you cannot assign them as such. These will fail:

type physics = scala.math
val physics = scala.math

With a package object, you can grab ahold of it's concrete members, but not the classes within. For example:

scala> val physics = scala.math.`package`
physics: math.type = scala.math.package$@42fcc7e6

scala> physics.min(1, 2)
res0: Int = 1

But using objects or types that belong to the traditional package won't work:

scala> scala.math.BigDecimal(1)
res1: scala.math.BigDecimal = 1

scala> physics.BigDecimal(1)
<console>:13: error: value BigDecimal is not a member of object scala.math.package
       physics.BigDecimal(1)
               ^

Ok, so what should you do?

The reason you're even considering this is that you want to hide the implementation of which library you're using so that it can easily be replaced later. If that's the case, what you should do is hide the library within another interface or object (a facade). It doesn't mean you need to forward every single method and value contained within the library, only the one's you're actually using. This way, when it comes to migrating to another library, you only need to change one class, because the rest of the code will only reference the facade.

For example, if we wanted to use min and max from scala.math, but later wanted to replace it with another library that provided a more efficient solution (if such a thing exists), we could create a facade like this:

object Math {
    def min(x: Int, y: Int): Int = scala.math.min(x, y)
    def max(x: Int, y: Int): Int = scala.math.max(x, y)
}

All other classes would use Math.min and Math.max, so that when scala.math was replaced, they could remain the same. You could also make Math a trait (sans implementations) and provide the implementations in a sub-class or object (say ScalaMath), so that classes could inject different implementations.

Upvotes: 12

Jean Logeart
Jean Logeart

Reputation: 53839

Simply:

val renamedfunctions = functions

import renamedfunctions._

You can see it being done in the scala library itself: https://github.com/scala/scala/blob/2.12.x/src/library/scala/Predef.scala#L150

val Map = immutable.Map

Upvotes: 1

Related Questions