I am currently reading Scala in depth and I struggle with a point about existential types.
Using those sources : https://github.com/jsuereth/scala-in-depth-source/blob/master/chapter6/existential-types/existential.scala
with openjdk 7 and scala 2.10.3
The following instructions gives me a error :
val x = new VariableStore[Int](12)
val d = new Dependencies {}
val t = x.observe(println)
d.addHandle(t)
<console>:14: error: method addHandle in trait Dependencies cannot be accessed in types.Dependencies
Access to protected method addHandle not permitted because
enclosing object $iw is not a subclass of
trait Dependencies in package types where target is defined
d.addHandle(t)
^
And I can't find out why and how I arrive to this error.
Edit 1 :
I added the following code from Kihyo's answer :
class MyDependencies extends Dependencies {
override def addHandle(handle: Ref) = super.addHandle(handle)
}
val x = new VariableStore[Int](12)
val d = new MyDependencies
val t = x.observe(println)
d.addHandle(t) //compiles
It make addHandle public instead of protected.
Now I have the following error message :
type mismatch; found : x.Handle (which expands to) x.HandleClass required: d.Ref (which
expands to) x.Handle forSome { val x: sid.types.obs.Observable }
HandleClass is a Handle and Ref is a Handle of any Observer (if I get it right) so the value t should be accepted as a correct type for the exception.
In the trait Dependencies, addHandle is defined like that:
protected def addHandle(handle : Ref) : Unit
protected means, only subclasses can access this method and thats why you get the error. (which basically tells you exactly that)
Your code could work when you create a subclass that makes addHandle public:
class MyDependencies extends Dependencies {
override def addHandle(handle: Ref) = super.addHandle(handle)
}
val x = new VariableStore[Int](12)
val d = new MyDependencies
val t = x.observe(println)
d.addHandle(t) //compiles
But I have no idea about that example and what you want to do with it.
#Edit1:
I get the same error as you, but I can't explain why. It worked for me when I extend App instead of having a main-method:
object TestObs extends App {
val x = new VariableStore[Int](12)
val d = new MyDependencies
val t = x.observe(println)
d.addHandle(t)
}
Maybe someone else has some insight on this.
Related
I have a situation where I'm trying to use implicit resolution on a singleton type. This works perfectly fine if I know that singleton type at compile time:
object Main {
type SS = String with Singleton
trait Entry[S <: SS] {
type out
val value: out
}
implicit val e1 = new Entry["S"] {
type out = Int
val value = 3
}
implicit val e2 = new Entry["T"] {
type out = String
val value = "ABC"
}
def resolve[X <: SS](s: X)(implicit x: Entry[X]): x.value.type = {
x.value
}
def main(args: Array[String]): Unit = {
resolve("S") //implicit found! No problem
}
}
However, if I don't know this type at compile time, then I run into issues.
def main(args: Array[String]): Unit = {
val string = StdIn.readLine()
resolve(string) //Can't find implicit because it doesn't know the singleton type at runtime.
}
Is there anyway I can get around this? Maybe some method that takes a String and returns the singleton type of that string?
def getSingletonType[T <: SS](string: String): T = ???
Then maybe I could do
def main(args: Array[String]): Unit = {
val string = StdIn.readLine()
resolve(getSingletonType(string))
}
Or is this just not possible? Maybe you can only do this sort of thing if you know all of the information at compile-time?
If you knew about all possible implementations of Entry in compile time - which would be possible only if it was sealed - then you could use a macro to create a map/partial function String -> Entry[_].
Since this is open to extending, I'm afraid at best some runtime reflection would have to scan the whole classpath to find all possible implementations.
But even then you would have to embed this String literal somehow into each implementations because JVM bytecode knows nothing about mappings between singleton types and implementations - only Scala compiler does. And then use that to find if among all implementations there is one (and exactly one) that matches your value - in case of implicits if there are two of them at once in the same scope compilation would fail, but you can have more than one implementation as long as the don't appear together in the same scope. Runtime reflection would be global so it wouldn't be able to avoid conflicts.
So no, no good solution for making this compile-time dispatch dynamic. You could create such dispatch yourself by e.g. writing a Map[String, Entry[_]] yourself and using get function to handle missing pices.
Normally implicits are resolved at compile time. But val string = StdIn.readLine() becomes known at runtime only. Principally, you can postpone implicit resolution till runtime but you'll be able to apply the results of such resolution at runtime only, not at compile time (static types etc.)
object Entry {
implicit val e1 = ...
implicit val e2 = ...
}
import scala.reflect.runtime.universe._
import scala.reflect.runtime
import scala.tools.reflect.ToolBox
val toolbox = ToolBox(runtime.currentMirror).mkToolBox()
def resolve(s: String): Any = {
val typ = appliedType(
typeOf[Entry[_]].typeConstructor,
internal.constantType(Constant(s))
)
val instanceTree = toolbox.inferImplicitValue(typ, silent = false)
val instance = toolbox.eval(toolbox.untypecheck(instanceTree)).asInstanceOf[Entry[_]]
instance.value
}
resolve("S") // 3
val string = StdIn.readLine()
resolve(string)
// 3 if you enter S
// ABC if you enter T
// "scala.tools.reflect.ToolBoxError: implicit search has failed" otherwise
Please notice that I put implicits into the companion object of type class in order to make them available in the implicit scope and therefore in the toolbox scope. Otherwise the code should be modified slightly:
object EntryImplicits {
implicit val e1 = ...
implicit val e2 = ...
}
// val instanceTree = toolbox.inferImplicitValue(typ, silent = false)
// should be replaced with
val instanceTree =
q"""
import path.to.EntryImplicits._
implicitly[$typ]
"""
In your code import path.to.EntryImplicits._ is import Main._.
Load Dataset from Dynamically generated Case Class
I am trying to convert from Guice to Macwire as a dependency injection framework. It is going fine apart from this Silhouette Module where I am getting a compilation error. Error at bottom.
Working Module in Guice:
class SilhouetteModule #Inject()(environment: play.api.Environment, configuration:
Configuration) extends AbstractModule with ScalaModule {
override def configure() = {
val iamConfig = IAMConfiguration
.fromConfiguration(configuration)
.fold(throw _, identity)
val htPasswdFile = File.apply(configuration.get[String]("file"))
bind[IdentityService[User]].toInstance(SimpleIdentityService.fromConfig(iamConfig))
bind[Silhouette[BasicAuthEnv]].to[SilhouetteProvider[BasicAuthEnv]]
bind[RequestProvider].to[BasicAuthProvider].asEagerSingleton()
bind[PasswordHasherRegistry].toInstance(PasswordHasherRegistry(new BCryptPasswordHasher()))
bind[AuthenticatorService[DummyAuthenticator]].toInstance(new DummyAuthenticatorService)
bind[AuthInfoRepository].toInstance(HtpasswdAuthInfoRepository.fromFile(htPasswdFile))
bind[SecuredErrorHandler].to[RestHttpSecuredErrorHandler]
}
#Provides
def provideEnvironment(identityService: IdentityService[User], authenticatorService:
AuthenticatorService[DummyAuthenticator], eventBus: EventBus, requestProvider:
RequestProvider): Environment[BasicAuthEnv] =
Environment[BasicAuthEnv](
identityService,
authenticatorService,
Seq(requestProvider),
eventBus
)
}
}
Equivalent attempt in Macwire:
trait SilhouetteModule extends BuiltInComponents {
import com.softwaremill.macwire._
val iamConfig = IAMConfiguration
.fromConfiguration(configuration)
.fold(throw _, identity)
val htPasswdFile = File.apply(configuration.get[String]("file"))
lazy val identityService: IdentityService[User] =
SimpleIdentityService.fromConfig(iamConfig)
lazy val basicAuthEnv: Silhouette[BasicAuthEnv] = wire[SilhouetteProvider[BasicAuthEnv]]
lazy val requestProvider: RequestProvider = wire[BasicAuthProvider]
lazy val passwordHasherRegistry: PasswordHasherRegistry = PasswordHasherRegistry(
new BCryptPasswordHasher())
lazy val authenticatorService: AuthenticatorService[DummyAuthenticator] =
new DummyAuthenticatorService
lazy val authInfoRepo: AuthInfoRepository =
HtpasswdAuthInfoRepository.fromFile(htPasswdFile)
lazy val errorHandler: SecuredErrorHandler = wire[RestHttpSecuredErrorHandler]
lazy val env: Environment[BasicAuthEnv] = Environment[BasicAuthEnv](
identityService,
authenticatorService,
Seq(requestProvider),
eventBus
)
def eventBus: EventBus
}
The Macwire example does not compile: I get an error:
Cannot find a value of type: [com.mohiva.play.silhouette.api.actions.SecuredAction]
lazy val basicAuthEnv: Silhouette[BasicAuthEnv] = wire[SilhouetteProvider[BasicAuthEnv]]
Sorry its a lot of code but I thought a side-by-side comparison would be more helpful.
Any help would be great!
MacWire doesn't magically creates values - if it needs to construct a value, it looks what values are taken by the constructor and - if by looking at all values available in the scope it can unambiguously find all the parameters of the constructor, the macro creates code new Class(resolvedArg1, resolvedArg2, ...).
So
all of these values have to be in Scope - they can be constructed by MacWire, or be abstract members implemented by some mixin, but still you have to write them down explicitly
if you have 2 values of the same type in scope - MacWire cannot generate the code because how it would know which value to pick? (Well, if one of the values is in closer "closer" than the other it can, but if they are equally close it cannot be resolved)
So if you get the error:
Cannot find a value of type: [com.mohiva.play.silhouette.api.actions.SecuredAction]
it means that you haven't declared any value of type com.mohiva.play.silhouette.api.actions.SecuredAction in SilhouetteModule nor in BuiltInComponents.
If this is something that is provided by another trait you can add abstract declaration here
val securedAction: SecuredAction // abstract val
and implement it somewhere else (be careful to avoid circular dependencies!).
I'm using sbt to build some of the riscv boom from the source code, but the sbt complains that it "could not find implicit value for parameter valName: : freechips.rocketchip.diplomacy.ValName". The detailed error message are as below:
[error] F:\hiMCU\my_proj\src\main\scala\freechips\rocketchip\tile\BaseTile.scala:170:42: could not find implicit value for parameter valName: freechips.rocketchip.diplomacy.ValName
[error] Error occurred in an application involving default arguments.
[error] protected val tlMasterXbar = LazyModule(new TLXbar)
The code where sbt complains is as below:
abstract class BaseTile private (val crossing: ClockCrossingType, q: Parameters)
extends LazyModule()(q)
with CrossesToOnlyOneClockDomain
with HasNonDiplomaticTileParameters
{
// Public constructor alters Parameters to supply some legacy compatibility keys
def this(tileParams: TileParams, crossing: ClockCrossingType, lookup: LookupByHartIdImpl, p: Parameters) = {
this(crossing, p.alterMap(Map(
TileKey -> tileParams,
TileVisibilityNodeKey -> TLEphemeralNode()(ValName("tile_master")),
LookupByHartId -> lookup
)))
}
def module: BaseTileModuleImp[BaseTile]
def masterNode: TLOutwardNode
def slaveNode: TLInwardNode
def intInwardNode: IntInwardNode // Interrupts to the core from external devices
def intOutwardNode: IntOutwardNode // Interrupts from tile-internal devices (e.g. BEU)
def haltNode: IntOutwardNode // Unrecoverable error has occurred; suggest reset
def ceaseNode: IntOutwardNode // Tile has ceased to retire instructions
def wfiNode: IntOutwardNode // Tile is waiting for an interrupt
protected val tlOtherMastersNode = TLIdentityNode()
protected val tlSlaveXbar = LazyModule(new TLXbar)
protected val tlMasterXbar = LazyModule(new TLXbar)
protected val intXbar = LazyModule(new IntXbar)
....
}
The LazyModule object code is as below:
object LazyModule
{
protected[diplomacy] var scope: Option[LazyModule] = None
private var index = 0
def apply[T <: LazyModule](bc: T)(implicit valName: ValName, sourceInfo: SourceInfo): T = {
// Make sure the user put LazyModule around modules in the correct order
// If this require fails, probably some grandchild was missing a LazyModule
// ... or you applied LazyModule twice
require (scope.isDefined, s"LazyModule() applied to ${bc.name} twice ${sourceLine(sourceInfo)}")
require (scope.get eq bc, s"LazyModule() applied to ${bc.name} before ${scope.get.name} ${sourceLine(sourceInfo)}")
scope = bc.parent
bc.info = sourceInfo
if (!bc.suggestedNameVar.isDefined) bc.suggestName(valName.name)
bc
}
}
I think the sbt should find some val of type freechips.rocketchip.diplomacy.ValName, but it didn't find such kind of val.
You need to have an object of type ValName in the scope where your LazyModules are instantiated:
implicit val valName = ValName("MyXbars")
For more details on Scala's implicit please see https://docs.scala-lang.org/tutorials/tour/implicit-parameters.html.html
You generally shouldn't need to manually create a ValName, the Scala compiler can materialize them automatically based on the name of the val you're assigning the LazyModule to. You didn't include your imports in your example, but can you try importing ValName?
import freechips.rocketchip.diplomacy.ValName
In most of rocket-chip code, this is imported via wildcard importing everything in the diplomacy package
import freechips.rocketchip.diplomacy._
OUTLINE
I have an API that looks something like this:
package com.example
object ExternalApi {
def create[T <: SpecialElement](elem: T): TypeConstructor[T] =
TypeConstructor(elem)
def create1[T <: SpecialElement](elem: T): TypeConstructor[T] =
TypeConstructor(elem)
def create2[T <: SpecialElement](elem: T): TypeConstructor[T] =
TypeConstructor(elem)
//...
}
object MyApi {
def process[T <: TypeConstructor[_ <: SpecialElement]](
l: T,
metadata: List[String]): T = {
println("I've been called!")
//do some interesting stuff with the List's type parameter here
l
}
}
case class TypeConstructor[E](elem: E)
trait SpecialElement
The ExternalApi (which is actually external to my lib, so no modifying that) has a series of calls that I'd like to automatically wrap with MyApi.process calls, with the metadata argument derived from the final type of T.
To illustrate, the calls to be wrapped could have any form, including nested calls, and calls within other AST subtree types (such as Blocks), e.g. :
package com.example.test
import com.example.{ExternalApi, SpecialElement}
object ApiPluginTest extends App {
//one possible form
val targetList = ExternalApi.create(Blah("name"))
//and another
ExternalApi.create2(ExternalApi.create1(Blah("sth")).elem)
//and yet another
val t = {
val sub1 = ExternalApi.create(Blah("anything"))
val sub2 = ExternalApi.create1(sub1.elem)
sub2
}
}
case class Blah(name: String) extends SpecialElement
Since compiler plugins handle matching structures within ASTs recursively "for free", I've decided to go with them.
However, due to the fact that I need to match a specific type signature, the plugin follows the typer phase.
Here's the code of the PluginComponent:
package com.example.plugin
import com.example.{SpecialElement, TypeConstructor}
import scala.tools.nsc.Global
import scala.tools.nsc.plugins.PluginComponent
import scala.tools.nsc.transform.Transform
class WrapInApiCallComponent(val global: Global)
extends PluginComponent
with Transform {
protected def newTransformer(unit: global.CompilationUnit) =
WrapInApiCallTransformer
val runsAfter: List[String] = List("typer") //since we need the type
val phaseName: String = WrapInApiCallComponent.Name
import global._
object WrapInApiCallTransformer extends Transformer {
override def transform(tree: global.Tree) = {
val transformed = super.transform(tree)
transformed match {
case call # Apply(_, _) =>
if (call.tpe != null && call.tpe.finalResultType <:< typeOf[
TypeConstructor[_ <: SpecialElement]]) {
println(s"Found relevant call $call")
val typeArguments = call.tpe.typeArgs.map(_.toString).toList
val listSymbOf = symbolOf[List.type]
val wrappedFuncSecondArgument =
q"$listSymbOf.apply(..$typeArguments)"
val apiObjSymbol = symbolOf[com.example.MyApi.type]
val wrappedCall =
q"$apiObjSymbol.process[${call.tpe.finalResultType}]($call, $wrappedFuncSecondArgument)"
//explicit typing, otherwise later phases throw NPEs etc.
val ret = typer.typed(wrappedCall)
println(showRaw(ret))
println("----")
ret
} else {
call
}
case _ => transformed
}
}
}
}
object WrapInApiCallComponent {
val Name = "api_embed_component"
}
This seems to resolve identifiers, as well as types, correctly, outputting e.g.:
Apply(TypeApply(Select(TypeTree().setOriginal(Ident(com.example.MyApi)), TermName("process")), List(TypeTree())), List(Apply(TypeApply(Select(Select(Select(Ident(com), com.example), com.example.MyApi), TermName("create")), List(TypeTree())), List(Apply(Select(Ident(com.example.test.Blah), TermName("apply")), List(Literal(Constant("name")))))), Apply(TypeApply(Select(TypeTree().setOriginal(Ident(scala.collection.immutable.List)), TermName("apply")), List(TypeTree())), List(Literal(Constant("com.example.test.Blah"))))))
Unfortunately, I get an error during compilation starting with:
scala.reflect.internal.FatalError:
[error]
[error] Unexpected tree in genLoad: com.example.MyApi.type/class scala.reflect.internal.Trees$TypeTree at: RangePosition([projectpath]/testPluginAutoWrap/compiler_plugin_test/src/main/scala/com/example/test/ApiPluginTest.scala, 108, 112, 112)
[error] while compiling: [projectpath]/testPluginAutoWrap/compiler_plugin_test/src/main/scala/com/example/test/ApiPluginTest.scala
[error] during phase: jvm
[error] library version: version 2.12.4
[error] compiler version: version 2.12.4
[error] reconstructed args: -Xlog-implicits -classpath [classpath here]
[error]
[error] last tree to typer: TypeTree(class String)
[error] tree position: line 23 of [projectpath]/testPluginAutoWrap/compiler_plugin_test/src/main/scala/com/example/test/ApiPluginTest.scala
QUESTION
It looks I'm screwing something up with type definitions, but what is it?
Specifically:
How do I correctly wrap every ExternalApi.createX call with an MyApi.process call, constrained by the requirements provided above?
NOTES
Given the amount of boilerplate required, I've set up a complete example project. It's available here.
The answer does not have to define a compiler plugin. If you're able to cover all the relevant calls with a macro, that is fine as well.
Originally the wrapped call was to something like: def process[T <: TypeConstructor[_ <: SpecialElement] : TypeTag](l: T): T, the setup here is actually a workaround. So if you are able to generate a wrapped call of this type, i.e. one that includes a runtime TypeTag[T], that's fine as well.
Background
I'm trying to add a #Prisms annotation to Monocle that will work as follows. Given:
#Prisms sealed trait Foo
case object A extends Foo
case object B extends Foo
it will generate a companion object for Foo:
object Foo {
val a = monocle.macros.GenPrism.apply[Foo, A]
val b = monocle.macros.GenPrism.apply[Foo, B]
}
(or if the companion object already exists, it will add those methods to it).
The macro relies on directKnownSubclasses to find A and B, so there will be a note in the Monocle readme recommending that people use Typelevel Scala. I've configured Monocle to use TLS on my branch.
First attempt
I tried using TypeNames to represent the parent type Foo and child type (A or B):
def findSubclasses(typeName: TypeName): Set[TypeName] = {
// Note: disable macros to prevent stack overflow caused by infinite typing loop!
val tpe = c.typecheck(Ident(typeName), mode = c.TYPEmode, silent = true, withMacrosDisabled = true)
tpe.symbol.asClass.knownDirectSubclasses.map(_.asType.name)
}
def prisms(parentTypename: TypeName, childNames: Set[TypeName]): Set[Tree] = childNames.map { childName =>
val prismName = TermName(prefix + childName.decodedName.toString.toLowerCase)
q"""val $prismName = monocle.macros.GenPrism.apply[$parentTypename, $childName]"""
}
This generates what looks like reasonable code:
{
sealed trait Foo;
object Foo {
val a = monocle.macros.GenPrism.apply[Foo, A];
val b = monocle.macros.GenPrism.apply[Foo, B]
};
()
}
But it doesn't seem to work:
[error] /Users/chris/code/Monocle/test/shared/src/test/scala/other/PrismsAnnotationSpec.scala:5: not found: type A
[error] #monocle.macros.Prisms sealed trait Foo
[error] ^
[error] one error found
Second attempt
I also tried using TypeSymbols instead of TypeNames:
def prisms(parentTypename: TypeName, childSymbols: Set[TypeSymbol]): Set[Tree] = {
val parentSymbol: TypeSymbol =
c.typecheck(Ident(parentTypename), mode = c.TYPEmode, silent = true, withMacrosDisabled = true)
.symbol
.asType
childSymbols.map { childSymbol =>
val prismName = TermName(prefix + childSymbol.name.decodedName.toString.toLowerCase)
q"""val $prismName = monocle.macros.GenPrism.apply[$parentSymbol, $childSymbol]"""
}
}
But this gives me the following error, so I'm guessing the generated tree is invalid:
[error] /Users/chris/code/Monocle/test/shared/src/test/scala/other/PrismsAnnotationSpec.scala:5: macro annotation could not be expanded (the most common reason for that is that you need to enable the macro paradise plugin; another possibility is that you try to use macro annotation in the same compilation run that defines it)
[error] #monocle.macros.Prisms sealed trait Foo
[error] ^
[error] one error found
Third attempt
I also tried turning the TypeSymbols into Types:
def prisms(parentTypename: TypeName, childSymbols: Set[TypeSymbol]): Set[Tree] = {
val parentSymbol: TypeSymbol =
c.typecheck(Ident(parentTypename), mode = c.TYPEmode, silent = true, withMacrosDisabled = true)
.symbol
.asType
childSymbols.map { childSymbol =>
val prismName = TermName(prefix + childSymbol.name.decodedName.toString.toLowerCase)
q"""val $prismName = monocle.macros.GenPrism.apply[${parentSymbol.toType}, ${childSymbol.toType}]"""
}
}
But I get the same error, saying the annotation could not be expanded.
Now I'm out of ideas.
How to reproduce
The complete code is available on this branch: https://github.com/cb372/Monocle/tree/sealed-trait-macro-annotation.
First attempt
Second attempt
Third attempt
The macro is used in test/shared/src/test/scala/other/PrismsAnnotationSpec.scala. To compile it, run sbt test:compile.