I am using a macro annotation from Spotify's Scio library. I would like to define a variable of String type and annotate like this:
val schemaString = """schema here"""
#BigQueryType.fromSchema(outputString) class BigQuery
This does not compile, however, if I annotate the String directly, it works:
#BigQueryType.fromSchema("""schema here""") class BigQuery
Looking at the code, this matching is done here, essentially the code is as follows:
def str(tree: c.Tree) = tree match {
// "string literal"
case Literal(Constant(s: String)) => s
// "string literal".stripMargin
case Select(Literal(Constant(s: String)), TermName("stripMargin")) => s.stripMargin
case _ => c.abort(c.enclosingPosition, errorMessage)
}
The question is why this does not match the variable, but does the string? And if there is any way to make the first example work?
The problem is that the value of string variables might not be available at compile-time, when macros are executed. What if it was:
val schemaString = doSomeComplexFunction()
#BigQueryType.fromSchema(schemaString) class BigQuery
In theory, maybe the macro could search for where the val is defined and allow it to work if it's just assigned a literal value, but even that can get complex if you start to think about scope.
So no, there's probably no way to get the first example to work.
Related
I am following this tutorial on GraphQL with Sangria. I am wondering about the following line
val JsObject(fields) = requestJSON
where requestJSON is an object of JsValue. This way of assigning fields is new to me and my question is, if you could name that pattern or provide me with a link to a tutorial regarding this structure.
The important thing to know is that val definitions support a Pattern on the left-hand side of the assignment, thus providing (subset of the functionality of) Pattern Matching.
So, your example is equivalent to:
val fields = requestJSON match {
case JsObject(foo) => foo
}
See Scala Language Specification Section 4.1 Value Declarations and Definitions for details.
So, for example, if you have a list l and you want to assign the first element and the rest, you could write:
val x :: xs = l
Or, for the fairly common case where a method returns a tuple, you could write:
val (result1, result2) = foo()
It is the Extractor pattern, you can reach the same result implementing the unapply method on your arbitrary object (like shown in the example). When you create a case class the compiler produces an unapply method for you, so you can do:
case class Person(name : String, surname : String)
val person = Person("gianluca", "aguzzi")
val Person(name, surname) = person
I'm thinking if its possible to create some sort of data structure that would take a string as a parameter and if that string does not contains .txt or something like that it would give me a compile error?
I can be specific on my problem - I am trying to take a file as a parameter(Source) to a function but I am told that currently I am passing any kind of file and it only should take text files so it is not done correctly. How do i approach this?
-Thanks!! ^^
Yes, this is not a problem. The big question is should you do it though. If your code is a library of sorts then under certain circumstances this might make sense. Check out Uri.uri from the http4s project
In your case the implementation could look something like this:
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
class Macros(val c: Context) {
import c.universe._
def txtFile(fileName: c.Expr[String]): Tree =
fileName.tree match {
case Literal(Constant(s: String)) if s.endsWith(".txt") =>
fileName.tree
case _ =>
c.abort(
c.enclosingPosition,
s"Supplied parameter is not a text file"
)
}
}
object MyObject {
def txtFile(fileName: String): String = macro Macros.txtFile
}
You'd use this like:
val x: String = txtFile("abc.tx") // Fails to compile
val y: String = txtFile("abc.txt") // Compiles
Again, it can be done, but you probably want to go with Option :-)
Assuming that String is coming from somewhere other than the code base (web request, cmd line), you'll need to pass that String to something that accepts Strings. Thus you'll have a point at which you're converting a String to your special filename datatype. So I can't see any way you'll be able to protect against some kind of runtime check.
If you want a runtime check that will avoid an error, you could return an Option, returning None if the String isn't a valid filename
You could use the refined library:
type TxtSuffix = EndsWith[W.`".txt"`.T]
type TxtFile = String Refined TxtSuffix
In order to construct a TxtFile, you need to give it a string that ends with ".txt":
val file: TxtFile = refineMV[TxtSuffix]("some.pdf") // compiler error
error: Predicate failed: "some.pdf".endsWith(".txt")
Excuse the long set-up. This question relates to, but is not answered by, Scala: ambiguous reference to overloaded definition - best disambiguation? .
I'm pretty new to Scala, and one thing that's throwing me off is that Scala both:
Has first-class functions
Calls functions when using object-dot notation without any parenthetical argument lists (as if the function were a property)
These two language features are confusing me. Look at the below code:
class MyClass {
def something(in: String): String = {
in + "_X"
}
def something: String => String = {
case _ => "Fixed"
}
}
val my = new MyClass()
println(List("foo", "bar").map(my.something))
I would expect this to print List("foo_X", "bar_X") by calling the something prototype that matches the map's required String => ? argument. Instead, the output is List("Fixed", "Fixed") - Scala 2.11 is invoking the no-argument something() and then passing its return value to the map.
If we comment out the second no-argument prototype of something, the output changes to be the expected result, demonstrating that the other prototype is valid in context.
Adding an empty argument list to the second prototype (making it def something()) also changes the behavior.
Changing the my.something to my.something(_) wakes Scala up to the ambiguity it was silently ignoring before:
error: ambiguous reference to overloaded definition,
both method something in class MyClass of type => String => String
and method something in class MyClass of type (in: String)String
match argument types (String)
println(List("foo", "bar").map(my.something(_)))
Even using the supposedly-for-this-purpose magic trailing underscore doesn't work:
val myFun: (String) => String = my.something _
This results in:
error: type mismatch;
found : () => String => String
required: String => String
val myFun: (String) => String = my.something _
My questions:
If I have MyClass exactly as written (no changes to the prototypes, especially not adding an empty parameter list to one of the prototypes), how do I tell Scala, unambiguously, that I want the first one-argument version of something to pass as an argument to another call?
Since there are clearly two satisfying arguments that could be passed to map, why did the Scala compiler not report the ambiguity as an error?
Is there a way to disable Scala's behavior of (sometimes, not always) treating foo.bar as equivalent to foo.bar()?
I have filed a bug on the Scala issue tracker and the consensus seems to be that this behaviour is a bug. The compiler should have thrown an error about the ambiguous reference to "my.something".
I have this weird requirement where data comes in as name ->value pair from a service and all the name-> value type is string only (which really they are not but that's how data is stored)
This is a simplified illustration.
case class EntityObject(type:String,value:String)
EntityObject("boolean","true")
now when getting that EntityObject if type is "boolean" then I have to make sure value is not anything else but boolean so first get type out and check value and cast value to that type. e.g in this case check value is boolean so have to cast string value to boolean to validate. If it was anything else besides boolean then it should fail.
e.g. if data came in as below, casting will fail and it should report back to the caller about this error.
EntityObject("boolean","1")
Due to this weird requirement it forces type conversion in validation code which doesn't look elegant and against type safe programming. Any elegant way to handle this in scala (may be in a more type safe manner)?
Here is where I'm going to channel an idea taken from a tweet by Miles Sabin in regards to hereogenous mappings (see this gist on github.) If you know the type of object mapping names a head of time you can use a nifty little trick which involves dependent types. Hold on, 'cause it's a wild ride:
trait AssocConv[K] { type V ; def convert: String => V }
def makeConv[V0](name: String, con: String => V0) = new AssocConv[name.type]{
V = V0
val convert = con
}
implicit val boolConv = makeConv("boolean", yourMappingFunc)
def convEntity(name: String, value: String)(implicit conv: AssocConv[name.type]): Try[conv.V] = Try{ conv.convert(value) }
I haven't tested this but it "should" work. I've also enclosed it in a Scala Try so that it catches exceptions thrown by your conversion function (in case you're doing things like _.toInt as the converter.)
You're really talking about conversion, not casting. Casting would be if the value really were an instance of Boolean at runtime, whereas what you have is a String representation of a Boolean.
If you're already working with a case class, I think a pattern matching expression would work pretty well here.
For example,
def convert(entity : EntityObject) : Any = entity match {
case EntityObject("boolean", "true") => true
case EntityObject("boolean", "false") => false
case EntityObject("string", s) => s
// TODO: add Regex-based matchers for numeric types
}
Anything that doesn't match one of the specified patterns would cause a MatchError, or you could put a catchall expression at the end to throw your own exception.
In this particular example, since the function returns Any, the calling coffee would need to do an actual type cast to get the specific type, but at least by that point all validation/conversion would have already been performed. Alternatively, you could just put the code that uses the values directly into the above function and avoid casting. I don't know what your specific needs are, so I can't offer anything more detailed.
I've been working with Scala Macros and have the following code in the macro:
val fieldMemberType = fieldMember.typeSignatureIn(objectType) match {
case NullaryMethodType(tpe) => tpe
case _ => doesntCompile(s"$propertyName isn't a field, it must be another thing")
}
reify{
new TypeBuilder() {
type fieldType = fieldMemberType.type
}
}
As you can see, I've managed to get a c.universe.Type fieldMemberType. This represents the type of certain field in the object. Once I get that, I want to create a new TypeBuilder object in the reify. TypeBuilder is an abstract class with an abstract parameter. This abstract parameter is fieldType. I want this fieldType to be the type that I've found before.
Running the code shown here returns me a fieldMemberType not found. Is there any way that I can get the fieldMemberType to work inside the reify clause?
The problem is that the code you pass to reify is essentially going to be placed verbatim at the point where the macro is being expanded, and fieldMemberType isn't going to mean anything there.
In some cases you can use splice to sneak an expression that you have at macro-expansion time into the code you're reifying. For example, if we were trying to create an instance of this trait:
trait Foo { def i: Int }
And had this variable at macro-expansion time:
val myInt = 10
We could write the following:
reify { new Foo { def i = c.literal(myInt).splice } }
That's not going to work here, which means you're going to have to forget about nice little reify and write out the AST by hand. You'll find this happens a lot, unfortunately. My standard approach is to start a new REPL and type something like this:
import scala.reflect.runtime.universe._
trait TypeBuilder { type fieldType }
showRaw(reify(new TypeBuilder { type fieldType = String }))
This will spit out several lines of AST, which you can then cut and paste into your macro definition as a starting point. Then you fiddle with it, replacing things like this:
Ident(TypeBuilder)
With this:
Ident(newTypeName("TypeBuilder"))
And FINAL with Flag.FINAL, and so on. I wish the toString methods for the AST types corresponded more exactly to the code it takes to build them, but you'll pretty quickly get a sense of what you need to change. You'll end up with something like this:
c.Expr(
Block(
ClassDef(
Modifiers(Flag.FINAL),
anon,
Nil,
Template(
Ident(newTypeName("TypeBuilder")) :: Nil,
emptyValDef,
List(
constructor(c),
TypeDef(
Modifiers(),
newTypeName("fieldType"),
Nil,
TypeTree(fieldMemberType)
)
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
)
)
Where anon is a type name you've created in advance for your anonymous class, and constructor is a convenience method I use to make this kind of thing a little less hideous (you can find its definition at the end of this complete working example).
Now if we wrap this expression up in something like this, we can write the following:
scala> TypeMemberExample.builderWithType[String]
res0: TypeBuilder{type fieldType = String} = $1$$1#fb3f1f3
So it works. We've taken a c.universe.Type (which I get here from the WeakTypeTag of the type parameter on builderWithType, but it will work in exactly the same way with any old Type) and used it to define the type member of our TypeBuilder trait.
There is a simpler approach than tree writing for your use case. Indeed I use it all the time to keep trees at bay, as it can be really difficult to program with trees. I prefer to compute types and use reify to generate the trees. This makes much more robust and "hygienic" macros and less compile time errors. IMO using trees must be a last resort, only for a few cases, such as tree transforms or generic programming for a family of types such as tuples.
The tip here is to define a function taking as type parameters, the types you want to use in the reify body, with a context bound on a WeakTypeTag. Then you call this function by passing explicitly the WeakTypeTags you can build from universe Types thanks to the context WeakTypeTag method.
So in your case, that would give the following.
val fieldMemberType: Type = fieldMember.typeSignatureIn(objectType) match {
case NullaryMethodType(tpe) => tpe
case _ => doesntCompile(s"$propertyName isn't a field, it must be another thing")
}
def genRes[T: WeakTypeTag] = reify{
new TypeBuilder() {
type fieldType = T
}
}
genRes(c.WeakTypeTag(fieldMemberType))