I am trying to create a custom field type in Squeryl. This field represents an Isin code, so it is backed up by a string field. Following the example on the documentation, I have added a simple validation before creating a new isin (never mind what an Isin code is or how the validation procedure works):
trait Domain[A] { self: CustomType[A] =>
def validate(a: A): Unit
validate(value)
}
class Isin(v: String) extends StringField(v) with Domain[String] {
println("instantiating Isin")
override def validate(s: String) {
println("calling validate with " + s)
assert(checkIsin(s))
}
private def checkIsin(isin: String): Boolean = {
// never mind the exact procedure
}
}
I have added some println to find out what is going on. I use this field inside a model like
case class Asset(
val id: Long = 0,
val isin: Isin
) extends KeyedEntity[Long]
object Asset {
import Database.assets
def create(isinCode: String) {
inTransaction {
assets.insert(new Asset(isin = new Isin(isinCode)))
}
}
}
Now, when I call Asset.create("US0378331005") (a valid ISIN) I get an exception. In the stacktrace it turns out that this exception is due to a call to the init method on a null value, which is supposedly passed to checkIsin. Indeed, the println statements print
calling validate with US0378331005
Instantiating Isin
calling validate with
So it seems that the validate method is actually invoked twice, but the second time it gets a null value.
What is going on wrong?
There are several problems here. First off, you seem to be using Lift Record and, if that's the case, you are implementing your validation logic incorrectly. The correct way to validate a Record field is to override
def validations: List[ValidationFunction]
where ValidationFunction is a type alias
type ValidationFunction = ValueType => List[FieldError]
and in your case ValueType == String.
The next issue is your Domain trait. Because your call to validate is inlined into the class definition, it will be called when your field is constructed. Obviously there is no value set at that point, so that's why you are seeing the println that references an empty value. I imagine that the order you see them in has something to do with your application flow. You have an existing record that is validated, and after that a new Record is created which triggers the next 2 println statements.
Related
I would like to declare some auxiliary values inside a case class constructor, but it seems not to be correct Scala.
In short, the following piece of code is correct:
case class Something(
text1: String,
text2: String
) {
def this(datetime: LocalDateTime) {
this(
s"date: ${datetime.toLocalDate.toString()}",
s"time: ${datetime.toLocalTime.toString()}"
)
}
}
and the following is not:
case class Something(
text1: String,
text2: String
) {
def this(datetime: LocalDateTime) {
val date = datetime.toLocalDate.toString()
val time = datetime.toLocalTime.toString()
this(
s"date: $date",
s"time: $time"
)
}
}
even though the latter would be more legible and easier to maintain. (Imagine using more complex operations than just calling two methods.) Why is that?
Is there another way to write a constructor like that or a way to work around this?
In Scala first call must be to primary constructor. After that you can have as much code as you want. Read this for explanation.
Similar rule applies to Java for this and super. Not exactly same though. Read this.
The reason why this and super must be first is, that one can set fields to various values before that actual this(x, y) is called. This means object is being constructed and different values can be visible to any thread that may have reference to the object while construction is in progress.
Thanks.
In your second case you are not allowed to define variables inside constructor before this(params) call, as computing inside constructors are discouraged in scala class or case class. One way you can fix it is pass inline constructor params.
test("test case class custom constructor") {
case class Something(text1: String,text2: String) {
def this(datetime: LocalDateTime) {
this(datetime.toLocalDate.toString(), datetime.toLocalTime.toString())
//you can do whatever you want after this(x, y) is invoked
val testVal = "apple"
println(testVal)
}
}
new Something(LocalDateTime.now()).text1 shouldBe "2017-07-16"
new Something(LocalDateTime.now()).text2 should not be empty
}
Another way (Encouraged way) is define case class and then define apply inside a companion object as below (for older version maybe 2.11.8, companion object had to be defined first and only case class which seems to be fixed now - https://issues.scala-lang.org/browse/SI-3772)
test("test class with companion apply method") {
case class Something(val text1: String, val text2: String) {}
object Something {
def apply(datetime: LocalDateTime): Something = {
val x = datetime.toLocalDate.toString()
val y = datetime.toLocalTime.toString()
new Something(x, y)
}
}
Something(LocalDateTime.now()).text1 shouldBe "2017-07-16"
Something(LocalDateTime.now()).text2 should not be empty
}
scastie code - https://scastie.scala-lang.org/prayagupd/yn2bJWHkQ6Gbli5Ll6I6CQ/1
Auxiliary constructors have a constraint that it should call a previous auxiliary constructor or primary constructor on the first line of its body. The second code does not follow that rule. Hence the error.
I am learning the scala and I have try with the following code.
object Demo7 {
def main(args: Array[String]): Unit = {
class Person(val fullName: String) {
println(s"This is the primary constructor. Name is ${fullName}")
val initial = fullName.substring(0, 1) // Computed during initialization
//def this(firstName: String, lastName: String) = this(s"$firstName $lastName")
}
new Person("Tek Tuk")
new Person("Tek Tuk").fullName
}
}
then I run I get the same returned result as each call.
for my understanding this line
new Person("Tek Tuk").fullName
Shouldn't compile, anyone can explain me why this line get compile and return the same result as the first line?
Thank you.
If you're asking why you're allowed to access the fullName field of your Person class, that's because you've declared it as a val in the parameter list.
This is the same as declaring it a public final field in Java. If you want it to be private, just remove the val part, i.e.
class Person(fullName: String) {
(...)
}
As for why both calls "return" the same thing - they don't.
new Person("Tek Tuk") returns an instance of Person.
new Person("Tek Tuk").fullName returns "Tek Tuk" - a String. You created another instance of Person with the same fullName and you called fullName on it.
Both, however, print "This is the primary constructor. Name is Tek Tuk", because you called the same constructor in both cases and you have a println that prints this in the constructor.
Note: There's an EDIT below!
Note: There's another EDIT below!
I have written a Scala annotation macro that is being passed a class and creates (or rather populates) a case object. The name of the case object is the same as the name of the passed class. More importantly, for every field of the passed class, there will be a field in the case object of the same name. The fields of the case object, however, are all of type String, and their value is the name of the type of the respective field in the passed class. Example:
// Using the annotation macro to populate a case object called `String`
#RegisterClass(classOf[String]) case object String
// The class `String` defines a field called `value` of type `char[]`.
// The case object also has a field `value`, containing `"char[]"`.
println(String.value) // Prints `"char[]"` to the console
This, however, seems to only work with pre-defined classes such as String. If I define a case class A(...) and try to do #RegisterClass(classOf[A]) case object A, I get the following error:
[info] scala.tools.reflect.ToolBoxError: reflective compilation has failed:
[info]
[info] not found: type A
What have I done wrong? The code of my macro can be found below. Also, if someone notices un-idiomatic Scala or bad practices in general, I wouldn't mind a hint. Thank you very much in advance!
class RegisterClass[T](clazz: Class[T]) extends StaticAnnotation {
def macroTransform(annottees: Any*) =
macro RegisterClass.expandImpl[T]
}
object RegisterClass {
def expandImpl[T](c: blackbox.Context)(annottees: c.Expr[Any]*) = {
import c.universe._
val clazz: Class[T] = c.prefix.tree match {
case q"new RegisterClass($clazz)" => c.eval[Class[T]](c.Expr(clazz))
case _ => c.abort(c.enclosingPosition, "RegisterClass: Annotation expects a Class[T] instance as argument.")
}
annottees.map(_.tree) match {
case List(q"case object $caseObjectName") =>
if (caseObjectName.toString != clazz.getSimpleName)
c.abort(c.enclosingPosition, "RegisterClass: Annotated case object and class T of passed Class[T] instance" +
"must have the same name.")
val clazzFields = clazz.getDeclaredFields.map(field => field.getName -> field.getType.getSimpleName).toList
val caseObjectFields = clazzFields.map(field => {
val fieldName: TermName = field._1
val fieldType: String = field._2
q"val $fieldName = $fieldType"
})
c.Expr[Any](q"case object $caseObjectName { ..$caseObjectFields }")
case _ => c.abort(c.enclosingPosition, "RegisterClass: Annotation must be applied to a case object definition.")
}
}
}
EDIT: As Eugene Burmako pointed out, the error happens because class A hasn't been compiled yet, so a java.lang.Class for it doesn't exist. I have now started a bounty of 100 StackOverflow points for everyone who as an idea how one could get this to work!
EDIT 2: Some background on the use case: As part of my bachelor thesis I am working on a Scala DSL for expressing queries for event processing systems. Those queries are traditionally expressed as strings, which induces a lot of problems. A typical query would look like that: "select A.id, B.timestamp from pattern[A -> B]". Meaning: If an event of type A occurs and after that an event of type B occurs, too, give me the id of the A event and the timestamp of the B event. The types A and B usually are simple Java classes over which I have no control. id and timestamp are fields of those classes. I would like queries of my DSL to look like that: select (A.id, B.timestamp) { /* ... * / }. This means that for every class representing an event type, e.g., A, I need a companion object -- ideally of the same name. This companion object should have the same fields as the respective class, so that I can pass its fields to the select function, like so: select (A.id, B.timestamp) { /* ... * / }. This way, if I tried to pass A.idd to the select function, it would fail at compile-time if there was no such field in the original class -- because then there would not be one in the companion object either.
This isn't an answer to your macro problem, but it could be a solution to your general problem.
If you can allow a minor change to the syntax of your DSL this might be possible without using macro's (depending on other requirements not mentioned in this question).
scala> class Select[A,B]{
| def apply[R,S](fa: A => R, fb: B => S)(body: => Unit) = ???
| }
defined class Select
scala> def select[A,B] = new Select[A,B]
select: [A, B]=> Select[A,B]
scala> class MyA { def id = 42L }
defined class MyA
scala> class MyB { def timestamp = "foo" }
defined class MyB
scala> select[A,B](_.id, _.timestamp){ /* ... */ }
scala.NotImplementedError: an implementation is missing
I use the class Select here as a means to be able to specify the types of your event classes while letting the compiler infer the result types of the functions fa and fb. If your don't need those result types you could just write it as def select[A,B](fa: A => Any, fb: B => Any)(body: => Unit) = ???.
If necessary you can still implement the select or apply method as a macro. But using this syntax, you will no longer need to generate objects with macro annotations.
I have a case class representing my domain:
case class MyModel(rawValue: String, transformedValue: String)
rawValue maps to a value in the database and is properly parsed and bound. What I am trying to do is add transformedValue to my model: this value is just some arbitrary transformation that I perform on the rawValue. It does not map to any data in the database / query.
I have a parser (prior to adding transformedValue) that looks like this:
val parser = {
get[String]("rawValue") map {
case rawValue => MyModel(rawValue)
}
}
Since MyModel is immutible and I can't insert transformedValue into it after its been created, what and where is the best way to do and add this transformation (e.g. adding ad-hoc values to the Model) preferably without using vars?
Coming from Java, I would probably just have added a getTransformedValue getter to the domain class that performs this transformation on the rawValue attribute.
Since transformedValue seems to be a derived property, and not something that would be supplied in the constructor, you can define it as an attribute in the body of the function, possibly using the lazy qualifier so that it will only be computed on-demand (and just once for instance):
case class MyModel(rawValue: String) {
lazy val transformedValue: String = {
// Transformation logic, e.g.
rawValue.toLowerCase()
}
}
val is evaluated when defined; def is evaluated when called. A lazy val is evaluated when it is accessed the first time.
It may be appropriate to use lazy in the case of an expensive computation that is rarely needed, or when logic requires it. But for most cases a regular val should be used. Quoting:
lazy val is not free (or even cheap). Use it only if you absolutely need laziness for correctness, not for optimization.
I don't see why you wouldn't just do the transformation in the parser itself:
def transformation(rawValue: String): String = ...
val parser = {
get[String]("rawValue") map {
case rawValue => MyModel(rawValue, transformation(rawValue))
}
}
Or if you don't want to do it there for some reason, you can use copy to create a new copy of MyModel with a new value:
val model = MyModel("value", ..)
val modelWithTransform = model.copy(transformedValue = transformation(model.rawValue))
You could also overload apply to automatically apply the transformation within the companion object:
case class MyModel(rawValue: String, transformedValue: String)
object MyModel {
def apply(value: String): MyModel = MyModel(rawValue, transformation(rawValue))
val parser = {
get[String]("rawValue") map {
case rawValue => MyModel(rawValue)
}
}
}
MyModel may be immutable, but you can always create another copy of it with some values changed.
Turns out it was easier than I thought and the solution did look like what I said I would have done in java:
I just add a function to MyModel that does this transformation:
case class MyModel(rawValue: String) {
def transformedValue = {
// do the transformation here and return it
}
}
I am learning lift and mongodb. I encountered a problem as following. I will bypass some code for simplicity. Here is the code:
object User extends User with MetaMegaProtoUser[User] {
}
class User extends MegaProtoUser[User] {
def meta = User
//record the post list that user like
object likePostList extends MongoListField[User, ObjectId](this)
def test()
{
val list: = this.likePostList
println(list.length)
}
}
error: value length is not a member of object User.this.likePostList
this.likePostList.length
I can store the ObjectId data in MongoDB. But the MongoListField does not return a list. Why? How to use it as a List.
I try to add type case it to List[OjbectId] or List[String] but without luck.
val list: List[ObjectId] = this.likePostList.asInstanceOf[List[Object]]
Got error:
java.lang.ClassCastException: com.cosiin.model.User$likePostList$ cannot be cast to scala.collection.immutable.List
I think I am using MongoListField the wrong way. But I do not know how to use it.
Can anyone help? Thanks
Fields you declare in Lift Records are field objects, not the actual values contained within the field. In order to access the actual value you need to call:
this.likePostList.get
or if the field is optional
this.likePostList.valueBox
which returns a scala.Option like object.
If you think of it, this makes very much sense because you're actually declaring likePostList to be an instance of MongoListField when you type:
object likePostList extends MongoListField[User, ObjectId](this)
as opposed to
val likePostList: List[ObjectId] = ...
there's no magic Scala can do to automatically convert that to List[ObjectId]. It's the same as:
class Foo {
val bar = 3
object baz { val greeting = "hello" }
}
val foo = new Foo
println(foo.bar) // prints 3
println(foo.baz) // prints something like Foo$baz$#1d981b6a
println(foo.baz.greeting) // prints "hello"
P.S. in older versions of Lift, get was called is.