Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala – Make implicit value classes available in another scope

I have a package foo which contains class FStream. The package object of foo defines a few implicit value classes that provide extender methods for FStream. I would like to move these value classes out of the package object and into their own individual files, but I also want them to always be available when I use FStream (or preferably, when I use anything from foo package. Is it possible to accomplish this? I tried putting implicit value classes into other objects, but I can't extend from objects. Tried putting them in classes or traits, but implicit value classes can only be defined in other objects.

foo/FStream.scala

package foo

class FStream {
  def makeFoo(): Unit = ???
}

foo/package.scala

package foo

package object foo {

  // I want to move these definitions into separate files:

  implicit class SuperFoo(val stream: FStream) extends AnyVal {
    def makeSuperFoo(): Unit = ???
  }

  implicit class HyperFoo(val stream: FStream) extends AnyVal {
    def makeHyperFoo(): Unit = ???
  }
} 

bar/usage.scala

package bar

import foo._ // something nice and short that doesn't reference individual value classes

val x: FStream = ???
x.makeSuperFoo() // should work
x.makeHyperFoo() // should work
like image 578
Nikita Avatar asked Feb 09 '17 04:02

Nikita


1 Answers

I recommend you to read the mandatory tutorial first.

My solution is to use FStream's companion object. So you can just import FStream and get all the functionality. This also uses trait to separate files.

foo/FStream.scala

package foo

class FStream {
  def makeFoo(): Unit = ???
}

// companion provides implicit
object FStream extends FStreamOp

foo/FStreamOp.scala

package foo

// value class may not be a member of another class
class SuperFoo(val stream: FStream) extends AnyVal {
  def makeSuperFoo(): Unit = ???
}

class HyperFoo(val stream: FStream) extends AnyVal {
  def makeHyperFoo(): Unit = ???
}
trait FStreamOp {
  // you need to provide separate implicit conversion
  implicit def makeSuper(stream: FStream) = new SuperFoo(stream)
  implicit def makeHyper(stream: FStream) = new HyperFoo(stream)
}

usage.scala

import foo.FStream

object Main {
  def main(args: Array[String]): Unit = {
    val x: FStream = ???
    x.makeSuperFoo() // should work
    x.makeHyperFoo() // should work
  }
}
like image 185
Herrington Darkholme Avatar answered Oct 30 '22 16:10

Herrington Darkholme