I have a list of Symbols representing packages, objects and classes and want to import them in a macro context.
For packages and objects, this would mean a wildcard import and for classes it would mean a “standard” import.
Given a List[Symbol] consisting of some.package, some.Class and some.Object, how would I properly import those and how can I decide whether a “standard” or a wildcard import needs to be used?
My current approach is this:
def importPackageOrModuleOrClass(sym: Symbol): Import =
if (sym.isPackage || sym.isModule) // e. g. import scala._, scala.Predef
gen.mkWildcardImport(sym)
else // e. g. import java.lang.String
gen.mkImport(sym.enclosingPackage, sym.name, sym.name) // <--- ?????
The package/module import works, but the class import doesn't although it looks correct.
You need to get the "TermName" like this...
def importPackageOrModuleOrClass(sym: Symbol): Import =
if (sym.isPackage || sym.isModule)
gen.mkWildcardImport(sym)
else
gen.mkImport(sym.enclosingPackage, sym.name.toTermName, sym.name.toTermName)
You can grab more hints regarding importing, reflecting, etc. via the sourcecode at http://xuwei-k.github.io/scala-compiler-sxr/scala-compiler-2.10.0/scala/reflect/internal/Importers.scala.html
Related
i originally made scripts with many functions on 2 individual scala worksheets. i got them working and now want to tie these individual scripts together by importing and using them into a third file. from what i have read you can not simply import external scripts you must first make them into a class and put them into a package. so i tried that but i still couldn't import it
i know this may be a bit basic for this site but im struggling to find much scala documentation.
i think my problem might span from a missunderstanding of how packages work. the picture below might help.
my program example
adder.scala
package adder
class adder {
def add_to_this(AA:Int):Int={
var BB = AA + 1;
return BB
}
}
build.scala
package builder
class build {
def make_numbers(){
var a = 0;
var b = 0;}
}
main.sc
import adder
import builder
object main {
adder.adder.add_to_this(10);
}
the errors i get are
object is not a member of package adder
object is not a member of package builder
Classes in scala slightly differ from classes in java. If you need something like singleton, you'll want to use object instead of class i.e.:
package com.example
object Main extends App {
object Hide{
object Adder{
def addToThis(AA:Int):Int = AA + 1
}
}
object Example{
import com.example.Main.Hide.Adder
def run(): Unit = println(Adder.addToThis(10))
}
Example.run()
}
Consider objects like packages/modules which are also regular values. You can import an object by its full path, i.e. com.example.Main.Hide.Adder you can also import contents of an object by adding .{addToThis}, or import anything from object by adding ._ after an object.
Note that classes, traits and case classes could not be used as objects, you can't do anything with it unless you have an instance - there are no static modifier.
I have a situation where I want to include all types from a package. But one of these types clashes with my own type. Is it possible to import the whole package and then rename the type that clashes, after the fact, rather than individually import every type I need?
import somepackage.all._ // Contains A, B, C, etc
import somepackage.all.{A=>_A} // Can I change the name of A after import?
class A {
val a = new _A()
val b = new B()
val c = new C()
}
You can do both at the same time:
import somepackage.all.{A=>_A, _}
This will import everything except A which will be imported as _A instead.
In my Scala project, almost all my files have these imports:
import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._
import spire.math._
import spire.implicits._
import com.wix.accord._
import com.wix.accord.dsl._
import codes.reactive.scalatime._
import better.files._
import java.time._
import scala.collection.mutable
...
...
What is the best way to DRY this in Scala? Can I specify all of them for my project (using some kind of sbt plugin?) or at the package level?
I've seen a few approaches that kinda solve what you're looking for. Check out
Imports defined
https://github.com/mongodb/casbah/blob/master/casbah-core/src/main/scala/Implicits.scala
Small example of this approach:
object Imports extends Imports with commons.Imports with query.Imports with query.dsl.FluidQueryBarewordOps
object BaseImports extends BaseImports with commons.BaseImports with query.BaseImports
object TypeImports extends TypeImports with commons.TypeImports with query.TypeImports
trait Imports extends BaseImports with TypeImports with Implicits
#SuppressWarnings(Array("deprecation"))
trait BaseImports {
// ...
val WriteConcern = com.mongodb.casbah.WriteConcern
// More here ...
}
trait TypeImports {
// ...
type WriteConcern = com.mongodb.WriteConcern
// ...
}
Imports used
https://github.com/mongodb/casbah/blob/master/casbah-core/src/main/scala/MongoClient.scala
When they use this import object, it unlocks all your type aliases for you. For example, WriteConcern
import com.mongodb.casbah.Imports._
// ...
def setWriteConcern(concern: WriteConcern): Unit = underlying.setWriteConcern(concern)
Essentially they wrap up all the imports into a common Import object, then just use import com.mycompany.Imports._
Doobie does something similar where most of the end-users just import doobie.imports._
https://github.com/tpolecat/doobie/blob/series/0.3.x/yax/core/src/main/scala/doobie/imports.scala
Again, a sample from this pattern:
object imports extends ToDoobieCatchSqlOps with ToDoobieCatchableOps {
/**
* Alias for `doobie.free.connection`.
* #group Free Module Aliases
*/
val FC = doobie.free.connection
/**
* Alias for `doobie.free.statement`.
* #group Free Module Aliases
*/
val FS = doobie.free.statement
// More here ...
}
The main difference in this approach between the package object style is you get more control over what/when to import. I've used both patterns, usually a package object for common utility methods I'll need across an internal package. And for libraries, specifically the users of my code, I can attach certain implicit definitions to an import object like in doobie mentioned above that will unlock a DSL syntax for the user using a single import.
I would probably go with the scala.Predef approach: basically, alias the types and expose the objects I want to make available. So e.g.
package com.my
package object project {
type LocalDate = java.time.LocalDate
type LocalDateTime = java.time.LocalDateTime
type LocalTime = java.time.LocalTime
import scala.collection.mutable
type MutMap[A, B] = mutable.Map[A, B]
val MutMap = mutable.Map
// And so on....
}
Now, wherever you start a file with package com.my.project, all of the above will be automatically available. Btw, kudos also to #som-snytt for pointing this out.
My relevant imports are:
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.json.Jsonimport play.modules.reactivemongo.json._
import play.modules.reactivemongo.ReactiveMongoApi
import play.modules.reactivemongo.json.collection.JSONCollection
import reactivemongo.api.commands.WriteResult
import reactivemongo.extensions.json.dao.JsonDao
import reactivemongo.extensions.json.dsl.JsonDsl._
The code which causes problem is
myCollection.find(Json.obj("email" -> email)).one
gives ambiguous implicit values: both object BSONDoubleFormat in trait BSONFormats of type play.modules.reactivemongo.json.BSONDoubleFormat.type and object BSONStringFormat in trait BSONFormats of type play.modules.reactivemongo.json.BSONStringFormat.type match expected type play.api.libs.json.Reads[T] myCollection.find(Json.obj("email" -> email)).one
As I understand I need to somehow define which format object should be used. But I don't understand how this can be done. The other problem is that I'm using JSON objects not BSON's to store data in Mongo, thus I don't understand why it is complaining BSONDoubleFormat & BSONStringFormat objects.
If you look at the documentation and examples, you can see that the function is .one[T], not .one.
As you don't indicate the result type T, it cannot compile.
myCollection.find(Json.obj("email" -> email)).one[T]
I need to import different libraries and do slightly different things depending on the use of my source, ie for running and testing.
If I have various source files that look something like this for production:
import scala.slick.driver.PostgresDriver.simple._
import Database.threadLocalSession
case class SomeBusinessObject(...)
What is good way to change it into the following for testing (meaning I would like to use a different databse during testing):
import scala.slick.driver.h2.simple._
import Database.threadLocalSession
case class SomeBusinessObject(...)
Oh, and then there's also a database class to hold the key to whatever database I'm using:
import scala.slick.driver.PostgresDriver.simple._
// Use the implicit threadLocalSession
import Database.threadLocalSession
object DB{
val get = {
Database.forURL("jdbc:postgresql:test:slick",
driver="org.postgresql.Driver",
user="postgres",
password="xxxx")
}
}
I did some research, and it looks like the Cake pattern may hold the key for this type of thing, but it would be great if someone could get me started (if cake's the right approach)
UPDATE:
Since asking this question, I found exactly what I was looking for in the Slick examples. It really sets it apart neatly! I worked through the examples some weeks back, but could not recall where I saw this done.
You could build on the fact that scala allows you to use "import" nested in a class, object or trait (and not just at the root level):
An example with scala.collection.mutable.Map and scala.collection.immutable.Map (say we want to use the immutable version of Map in test and the mutable one in Production)
scala> trait Import1 {
| import scala.collection.mutable.{Map => iMap}
| def Test = iMap(1 -> "a")
| }
defined trait Import1
scala> trait Import2 {
| import scala.collection.immutable.{Map => iMap}
| def Test = iMap(1 -> "a")
| }
defined trait Import2
now you can do:
scala> object ProductionThing extends Import1
defined module ProductionThing
scala> ProductionThing.Test
res6: scala.collection.mutable.Map[Int,java.lang.String] = Map(1 -> a)
scala> object TestingThing extends Import2
defined module TestingThing
scala> TestingThing.Test
res7: scala.collection.immutable.Map[Int,java.lang.String] = Map(1 -> a)
or you could use the same mechanism to scope your import within a class and inject that class into SomeBusinessObject.