Scala – Make implicit value classes available in another scope - scala

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

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
}
}

Related

How to refer to "this" in an implicit method

Given a case class and companion object like below:
case class Example(a: String)
object Example {
implicit def concat(b: String): Example =
Example(this.a + b)
}
How to make the implicit method compile? In other words, is it possible to refer to the current instance that the implicit method has been called upon?
I think what you want is the following:
object Foo {
implicit class RichExample(val e: Example) {
def concat(b: String): Example = Example(e.a + b)
}
}
or using an anonymous implicit class
object Foo {
implicit def richExample(e: Example) = new {
def concat(b: String): Example = Example(e.a + b)
}
}
Usage
You can then use it like this
import Foo._
Example("foo").concat("bar")
Import and companion object
If the object is called Example, then it becomes the companion object for class Example, and you do not have to import Foo._ to use the extension method.

Implicit search inside macro expansion fails

Consider a trait that performs "encoding" of arbitrary objects:
trait Encoder[R] {
def encode(r: R): Array[Byte]
}
Assuming encoding of primitive types is known and custom types can be encoded by defining up a "serializer":
trait Serializer[T] {
def serialize(r: T): Array[Byte]
}
we can implement a macro for case class encoding by simply looping the fields and looking up type serializers implicitly. Here's a dummy implementation that looks up the serializer for R itself (in reality we look up serializers for case class field types):
object Encoder {
implicit def apply[R <: Product]: Encoder[R] = macro applyImpl[R]
def applyImpl[R: c.WeakTypeTag](c: blackbox.Context): c.Expr[Encoder[R]] = {
import c.universe._
c.Expr[Encoder[R]](q"""
new ${weakTypeOf[Encoder[R]]} {
override def encode(r: ${weakTypeOf[R]}): Array[Byte] =
implicitly[_root_.Serializer[${weakTypeOf[R]}]].serialize(r)
}
""")
}
}
Now define a base "processor":
abstract class BaseProcessor[R: Encoder] {
def process(r: R): Unit = {
println(implicitly[Encoder[R]].encode(r).length)
}
}
And try to use it:
case class Record(i: Int)
object Serializers {
implicit def recordSerializer: Serializer[Record] =
(r: Record) => Array.emptyByteArray
}
import Serializers._
class Processor extends BaseProcessor[Record]
This fails to compile with:
// [error] Loader.scala:10:22: could not find implicit value for parameter e: Serializer[Record]
// [error] class Processor extends BaseProcessor[Record]
// [error] ^
// [error] one error found
However the following do compile:
class Processor extends BaseProcessor[Record]()(Encoder[Record]) // Compiles!
object x { class Processor extends BaseProcessor[Record] } // Compiles!
I can't really understand why this happens, looks like it has something to do with the Processor definition being a top level definition, since as soon as I move it inside a class/object everything works as expected. Here's one more example that fails to compile:
object x {
import Serializers._ // moving the import here also makes it NOT compile
class Processor extends BaseProcessor[Record]
}
Why does this happen and is there any way to make it work?
To make it work you can try one of the following:
1) add context bound
implicit def apply[R <: Product : Serializer]: Encoder[R] = macro applyImpl[R]
def applyImpl[R: c.WeakTypeTag](c: blackbox.Context)(serializer: c.Expr[Serializer[R]]): c.Expr[Encoder[R]] = {
import c.universe._
c.Expr[Encoder[R]](q"""
new Encoder[${weakTypeOf[R]}] {
override def encode(r: ${weakTypeOf[R]}): Array[Byte] =
$serializer.serialize(r)
}
""")
}
2) make macro whitebox
3) put implicit instance recordSerializer: Serializer[Record] to the companion object of Record rather than some object Serializers to be imported
4) maybe your example is easier than actual use case but now it seems you don't need macros
implicit def apply[R <: Product : Serializer]: Encoder[R] = new Encoder[R] {
override def encode(r: R): Array[Byte] = implicitly[Serializer[R]].serialize(r)
}

how to use implicit method in trait

I am trying to understand the implicit method in Scala, in the SlashSyntax.scala I defined a customed operator method:
package com.example.hello.impl
object SlashSyntax {
final class IntOps(private val intSrc: Int) {
def \(a2: Int): Int = (intSrc.toString + a2.toString).toInt
}
}
trait SlashSyntax {
import SlashSyntax._
implicit def intOps(src: Int) = new IntOps(src)
}
then in the main method I try to use the operator but it says cannot resolve symbol :
object Test {
def main(args: Array[String]): Unit = {
import com.example.hello.impl.SlashSyntax._
val s = 10 \ 2 //cannot resolve symbol \
}
}
How can I use my own operator?
The simplest solution is to make the IntOps class an implicit class. Then
you don't need the SlashSyntax trait (or you can use it for other things)
the implicit is in the right place to be imported
the pesky implicit conversion method ... warning goes away
The issue caused by incorrect import.
import com.example.hello.impl.SlashSyntax._
Problem here is that you imported all members from object SlashSyntax instead of trait SlashSyntax. This happened because scala considered com.example.hello.impl.SlashSyntax as path. All paths can be composed only from Stable members. Traits are not Stable members but objects are stable members, so scala imported members from object SlashSyntax.
You can just move your implicit method to object SlashSyntax and this will fix the issue.
Your object SlashSyntax doesn't extends trait SlashSyntax so your extension methods are not seen when you do SlashSyntax._.
Additionally you didn't do import scala.languageFeatures.implicitConversions. though you might have added -language:implicitConversions flag to scalac.
Sometimes lack of type annotation break things. Not in your case but it's good in general to these things there.
This works:
object SlashSyntax extends SlashSyntax {
final class IntOps(private val intSrc: Int) {
def \(a2: Int): Int = (intSrc.toString + a2.toString).toInt
}
}
trait SlashSyntax {
import SlashSyntax.IntOps
implicit def intOps(src: Int): IntOps = new IntOps(src)
}

Could not find implicit value for parameter x

Just when I thought I understood the basics of Scala's type system... :/
I'm trying to implement a class that reads the contents of a file and outputs a set of records. A record might be a single line, but it could also be a block of bytes, or anything. So what I'm after is a structure that allows the type of Reader to imply the type of the Record, which in turn will imply the correct Parser to use.
This structure works as long as MainApp.records(f) only returns one type of Reader. As soon as it can return more, I get this error:
could not find implicit value for parameter parser
I think the problem lies with the typed trait definitions at the top, but I cannot figure out how to fix the issue...
// Core traits
trait Record[T]
trait Reader[T] extends Iterable[Record[T]]
trait Parser[T] {
def parse(r: Record[T]): Option[Int]
}
// Concrete implementations
class LineRecord[T] extends Record[T]
class FileReader[T](f:File) extends Reader[T] {
val lines = Source.fromFile(f).getLines()
def iterator: Iterator[LineRecord[T]] =
new Iterator[LineRecord[T]] {
def next() = new LineRecord[T]
def hasNext = lines.hasNext
}
}
trait TypeA
object TypeA {
implicit object TypeAParser extends Parser[TypeA] {
def parse(r: Record[TypeA]): Option[Int] = ???
}
}
trait TypeB
object TypeB {
implicit object TypeBParser extends Parser[TypeB] {
def parse(r: Record[TypeB]): Option[Int] = ???
}
}
// The "app"
object MainApp {
def process(f: File) =
records(f) foreach { r => parse(r) }
def records(f: File) = {
if(true)
new FileReader[TypeA](f)
else
new FileReader[TypeB](f)
}
def parse[T](r: Record[T])(implicit parser: Parser[T]): Option[Int] =
parser.parse(r)
}
First off you must import the implicit object in order to use them:
import TypeA._
import TypeB._
That's not enough though. It seems like you're trying to apply implicits dynamically. That's not possible; they have to be found compile time.
If you import the objects as above and change the records so that the compiler finds the correct generic it will run fine:
def records(f: File) = new FileReader[TypeA](f)
But then it may not be what you were looking for ;)
The problem is that the return type of your records method is basically FileReader[_] (since it can return either FileReader[TypeA] or FileReader[TypeB]), and you don't provide an implicit argument of type Parser[Any]. If you remove the if-expression the return type is inferred to FileReader[TypeA], which works fine. I'm not sure what you're trying to do, but obviously the compiler can't select implicit argument based upon a type that is only known at runtime.
1) Using type with implicit inside as type parameter - doesn't bind this implicit to the host type, to do this change objects to the traits and mix them instead of generalizing (type-parametrizing):
def records(f: File) = {
if(true)
new FileReader(f) with TypeA
else
new FileReader(f) with TypeB
}
2) The parser should be in scope of function that calls parse. So you may try smthg like that:
def process(f: File) = {
val reader = records(f);
import reader._
reader foreach { r => parse(r) }
}
PlanB) Simpler alternative is to define type-parameter specific implicit methods inside the AppMain (or some trait mixed in), but it will work only if TypeA/TypeB is known on compile time, so records method can return concrete type:
implicit class TypeAParser(r: Record[TypeA]) {
def parse: Option[Int] = ???
}
implicit class TypeBParser(r: Record[TypeB]) {
def parse: Option[Int] = ???
}
def process[T <: TypeAorB](f: File) =
records[T](f).foreach(_.parse)
def recordsA[T <: TypeAorB](f: File) = new FileReader[T](f)
Here is, I think, the full set of modifications you need to do to get where I think you want to go.
import scala.io.Source
import java.io.File
import reflect.runtime.universe._
// Core traits
trait Record[+T]
trait Reader[+T] extends Iterable[Record[T]]
trait Parser[-T] {
def parse(r: Record[T]): Option[Int]
}
// Concrete implementations [unmodified]
class LineRecord[T] extends Record[T]
class FileReader[T](f:File) extends Reader[T] {
val lines = Source.fromFile(f).getLines()
def iterator: Iterator[LineRecord[T]] =
new Iterator[LineRecord[T]] {
def next() = new LineRecord[T]
def hasNext = lines.hasNext
}
}
sealed trait Alternatives
case class TypeA() extends Alternatives
object TypeA {
implicit object TypeAParser extends Parser[TypeA] {
def parse(r: Record[TypeA]): Option[Int] = ???
}
}
case class TypeB() extends Alternatives
object TypeB {
implicit object TypeBParser extends Parser[TypeB] {
def parse(r: Record[TypeB]): Option[Int] = ???
}
}
class ParseAlternator(parserA: Parser[TypeA], parserB: Parser[TypeB]) extends Parser[Alternatives] {
def parse(r: Record[Alternatives]): Option[Int] = r match {
case x: Record[TypeA #unchecked] if typeOf[Alternatives] =:= typeOf[TypeA] => parserA.parse(x)
case x: Record[TypeB #unchecked] if typeOf[Alternatives] =:= typeOf[TypeB] => parserB.parse(x)
}
}
object ParseAlternator {
implicit def parseAlternator(implicit parserA: Parser[TypeA], parserB: Parser[TypeB]): Parser[Alternatives] = new ParseAlternator(parserA, parserB)
}
// The "app"
object MainApp {
import ParseAlternator._
def process(f: File) =
records(f) foreach { r => parse(r) }
def records(f: File): Reader[Alternatives] = {
if(true)
new FileReader[TypeA](f)
else
new FileReader[TypeB](f)
}
def parse[T](r: Record[T])(implicit parser: Parser[T]): Option[Int] =
parser.parse(r)
}
The gist of it is: all of this would be completely classsical if only your parse instance did not have to pattern-match on a generic type but dealt directly with an Alternative instead.
It's this limitation (inherited from the JVM) that scala can't properly pattern-match on an object of a parametric type that requires the reflection & typeOf usage. Without it, you would just have type alternatives for your content (TypeA, TypeB), which you would add to a sealed trait, and which you would dispatch on, in an implicit that produces a Parser for their supertype.
Of course this isn't the only solution, it's just what I think is the meeting point of what's closest to what you're trying to do, with what's most idiomatic.

Strange implicit resolution behaviour with type classes and package objects

I'm trying to provide different sets of implementations for a list of type classes, where importing different package objects would give the end user a different version of a TypeClass implementation into scope, with the swap being completely invisible.
trait TypeClass[T] {
def name: String
}
trait DefaultTypeClasses {
implicit val String: TypeClass[String]
implicit val Int: TypeClass[Int]
}
trait FirstImplementor extends DefaultTypeClasses {
implicit object String extends TypeClass[String] {
def name = "test"
}
implicit object Int extends TypeClass[Int] {
def name = "int"
}
}
object FirstImplementor extends FirstImplementor
object Test {
import FirstImplementor._
def doSomething[T : TypeClass](value: T): Unit = {
println(implicitly[TypeClass[T]].name)
}
doSomething("test")
}
The above works as expected, but if do:
trait DefaultDefinitions extends DefaultTypeClasses {
}
package object something extends DefaultDefinitions with FirstImplementor {}
And then I import the same package object into the scope of the Test object, like this:
import com.blabla.something._ // should bring all type class definitions from FirstImplementor into scope.
object Test {
def doSomething[T : TypeClass](value: T): Unit = {
println(implicitly[TypeClass[T]].name)
}
doSomething("test")// Cannot find implicit value for TypeClass[String]
doSomething[String]("test")(String) // if passed explicitly it compiles as expected, no more imports necessary.
}
For whatever reason, the materialised type class is available in explicit scope by name, but not in implicit scope. Sounds like my knowledge of the SLS is experiencing a gap, could anyone please clarify?
But if you create Implicits object in package, it works fine. However I have no idea if original error is a bug in scalac or correct behavior
package object something {
object Implicits extends DefaultDefinitions with FirstImplementor
}
import something.Implicits._