0

I'm working on some tooling that'll be loaded in the standard repl, or ammonite. Is there any way to compose package objects / imports so that I don't have to import the scala.concurent.duration package in my clients ? Is this the only way for me to reuse the implicit duration conversion ?

(yes I know an ammonite script wrapping my tooling can load everything for the REPL but ammonite might not be the only way I use the packages)

This wouldn't be a scaleable approach :(

package object tool package {
  // redeclaring the implicit value class conversions found in package object scala.concurrent.duration
  implicit final class DurationInt(private val n: Int) extends AnyVal with DurationConversions {
    override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(n.toLong, unit)
  }

  implicit final class DurationLong(private val n: Long) extends AnyVal with DurationConversions {
    override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(n, unit)
  }
}

1 Answer 1

1

Yes. Folks ask to "package imports" in this way, but it doesn't work that way.

Another common use case is importing the language implicits: you must redefine the values in your package.

The following doesn't work in REPL, but putting it here as a reminder for future:

scala> :pa -raw
// Entering paste mode (ctrl-D to finish)

// attempt to add an import into the block
package object w {
  def wrap[A](a: A): A = macro www[A]
  import reflect.macros.blackbox.Context
  def www[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]) = {
    import c.universe._
    val Block(ss, res) = a.tree
    Block(q"import scala.concurrent.duration._" +: ss, res)
  }
}

// Exiting paste mode, now interpreting.


scala> $intp.setExecutionWrapper("w.wrap")

It doesn't work because the wrapper wraps the wrong bit of code...

scala> 5.seconds
<console>:12: error: value seconds is not a member of Int
       5.seconds
         ^

scala> :se -Xprint:typer

scala> 42
[[syntax trees at end of                     typer]] // <console>
package $line7 {
  object $read extends scala.AnyRef {
    def <init>(): $line7.$read.type = {
      $read.super.<init>();
      ()
    };
    object $iw extends scala.AnyRef {
      def <init>(): type = {
        $iw.super.<init>();
        ()
      };
      object $iw extends scala.AnyRef {
        def <init>(): type = {
          $iw.super.<init>();
          ()
        };
        private[this] val res4: Int = 42;
        <stable> <accessor> def res4: Int = $iw.this.res4
      }
    }
  }
}

[[syntax trees at end of                     typer]] // <console>
package $line7 {
  object $eval extends scala.AnyRef {
    def <init>(): $line7.$eval.type = {
      $eval.super.<init>();
      ()
    };
    lazy private[this] var $result: Int = _;
    <stable> <accessor> lazy def $result: Int = {
      $eval.this.$result = $line7.$read.$iw.$iw.res4;
      $eval.this.$result
    };
    lazy private[this] var $print: String = _;
    <stable> <accessor> lazy def $print: String = {
      $eval.this.$print = ({
        import scala.concurrent.duration._;
        $line7.$read.$iw.$iw;
        "res4: Int = ".+(scala.runtime.ScalaRunTime.replStringOf($line7.$read.$iw.$iw.res4, 1000))
      }: String);
      $eval.this.$print
    }
  }
}

res4: Int = 42

...and anyway the code must compile before and after expansion.

The templating doesn't allow adding the trailing brace:

scala> $intp.setExecutionWrapper("{ import scala.concurrent.duration._ ")

It would have to look something like

scala> $intp.setExecutionWrapper("{ import scala.concurrent.duration._ %s }")

As you point out, scala -i my-imports.sc is for the general init, but something with fewer seams would be neat.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.