Say I have a case class:
case class Name(first: String, last: String)
val n1 = Name("John", "Doe")
val n2 = Name("Mary", "Doe")
and I have a java script object:
#js.native
trait MyJSObject extends js.Object {
def add(name: js.Object): Unit = js.native
}
such that name should be of the format
{"first":"John","last":"Doe"}
What is the best way to convert my case object to a js.Object?
I know this can be achieved doing a upickle.write() to convert it to a json String so I guess my question is is this the best way?
If you control the case class, you can just add the #JSExportAll annotation to it:
#JSExportAll
case class Name(first: String, last: String)
This will create properties named first and last. Note that this is not the same as a simple object. However, depending on what the add method does, it is sufficient (and much more lightweight than converting to JSON and parsing again).
In case you need to cross compile the case class, you can include the scalajs-stubs library in your JVM project. It will provide dummy annotations (that are not written to .class files).
If you need a js.Object, you can use the export typechecker:
val x = Name("foo", "bar")
js.use(x).as[js.Object]
This will even work with a trait that defines the fields you need:
#js.native
trait Options extends js.Object {
def first: String = js.native
def last: String = js.native
}
val x = Name("foo", "bar")
js.use(x).as[Options]
The js.use(x).as call will fail, if there is a mismatch between the types (i.e. if Options defines a field that Name doesn't define).
With uPickle:
val jsValue: Js.Value = upickle.Types.writeJs[Name](n1)
val jsObject: js.Any = upickle.json.writeJs(jsValue).asInstanceOf[js.Any]
Related
In scala I'm not allowed to perform the following:
case class Terminal(value: Double, name: String = value.toString)
Moreover I also cannot do this:
case class Terminal(value: Double)(name: String = value.toString)
I understand the multiple parameter list approach is not supported for constructors.
Is is there a way to define in the apply method in order to make this possible?
Expected behavior:
Terminal(1.0) // => Terminal (1.0, "1.0")
You can't do this in the case class itself, and it won't make a constructor, but it is possible through the apply method on the companion.
case class Terminal(value: Double, name: String)
object Terminal {
def apply(value: Double): Terminal = Terminal(value, value.toString)
}
Note that:
def apply(value: Double, name: String = value.toString) = new Terminal(value, name)
is an error because it conflicts with the autogenerated apply.
Maybe you just want this?
case class Terminal(value: Double) {
val name = a.toString
}
How can I achieve this:
final case class ChairId(id: String)
trait GeneratorLike[TO, TC <: AbstractId] {
val prefix: String
def generate(): TC = TO.apply(prefix + "-" + UUID.randomUUID())
}
implicit object ChairIdGenerator extends GeneratorLike[ChairId.type, ChairId] {
val prefix: String = "CHAIR"
}
implicit def IdFn[TO, TC <: AbstractId](x: TO)(implicit ev: GeneratorLike[TO, TC]): GeneratorLike[TO, TC] = ev
//right now I can call:
ChairId.generate()
I don't want to define companion object for that situation and I wondered if there is a chance to extend object with use of implicits?
When I do (I use TO as TypeObject and TC as TypeClass naming) idFn[TO, TC] I want TO to be object that implements def apply(id: String): TC can I enforce that? And how would I get to use this function? It feels totally impossible to call function on type parameter :/
It is impossible to call a method on a type parameter, because it represents a type and not an object. You can call a method on an object, because it is something that exists, but a type is an abstract concept. I don't know what your motivation is for wanting to implicitly add generate() to companion objects, because it actually requires just as much code to define an implicit GeneratorLike than it does to define the companion for ChairId.
If you force GeneratorLike to have an apply method (which can be implemented by case class apply), and remove the first type parameter, this will work.
trait GeneratorLike[TC <: AbstractId] { this: Singleton =>
val prefix: String
def apply(id: String): TC
def generate(): TC = apply(prefix + "-" + UUID.randomUUID())
}
abstract class AbstractId
final case class ChairId(id: String) extends AbstractId
object ChairId extends GeneratorLike[ChairId] {
val prefix = "CHAIR"
}
scala> ChairId.generate()
res0: ChairId = ChairId(CHAIR-60bb01c7-af95-46c7-af45-0b3fa78b3080)
Structural typing is not a particularly good idea on the JVM, so always try to avoid the def test(x: {def apply(s: String)}): TC type stuff because it is implemented using reflection which can be a dog performance wise.
Second, you should probably avoid using val inside a trait. Read here.
The approach you have considered is actually the right one, and namely type classes.
trait HasGenerator[T] {
def apply(uuid: String): T
def generate[T : Generator] = apply(Generator[T].generate)
}
final case class ChairId(id: String)
object ChairId extends HasGenerator[ChairId]
trait Generator[TO] {
def prefix: String
def generate(): String = prefix + "-" + UUID.randomUUID()
def apply(): String = generate
}
object Generator {
def apply[T : Generator] = implicitly[Generator[T]]
}
// Notice .type is not necessary
implicit object ChairIdGenerator extends Generator[ChairId] {
override def prefix = "CHAIR"
}
Why not just use:
ChairId(Generator[ChairId])
This all seems like overkill though so you can quite easily somehow. It's worth fleshing out your requirements a bit more because type classes don't really seem super necessary just yet. You could just do with:
Update
If you use something like the HasGenerator that I have added above in conjunction with the companion object, you can now successfully call ChairId.generate()
I'm using Scala 2.10.2, and have two case classes that have identical fields:
case class Foo(id: String, name: String)
case class Bar(id: String, name: String)
I'd like to do something like this:
case class Thing(id: String, name: String)
type Foo = Thing
type Bar = Thing
This compiles, but when I try to create a Foo, I get:
scala> Bar("a", "b")
<console>:8: error: not found: value Bar
Bar("a", "b")
^
Does type aliasing not work with case classes?
When you create a case class Scala automatically creates a companion object for it. In your code you define an alias for the type Thing, i.e. for the class Thing only. Your companion object Thing still has only 1 name and no aliases.
One way to "fix" it is to create a reference to the companion object (not a type alias) like this:
scala> val Bar = Thing
Bar: Thing.type = Thing
scala> Bar("a", "b")
res1: Thing = Thing(a,b)
Another way to "fix" it would be to rename the imported object with import package.{Thing => Bar}.
Type aliases only alias the type, not any companion object that might be supplying factory methods (whether you write that factory method yourself or get one "for free" from the compiler).
On the other hand, importing acts on names and if there are multiple entities associated with a given name, importing that name brings in every referent of the imported name. Additionally, you can rename when importing and you can do so multiply, so...
scala> object Stuff { case class Thing(id: String, name: String) }
defined module Stuff
scala> import Stuff.Thing
import Stuff.Thing
scala> import Stuff.{Thing => Foo}
import Stuff.{Thing=>Foo}
scala> import Stuff.{Thing => Bar}
import Stuff.{Thing=>Bar}
scala> val thing1 = Thing("fing", "fang")
thing1: Stuff.Thing = Thing(fing,fang)
scala> val foo1 = Foo("yes", "no")
foo1: Stuff.Thing = Thing(yes,no)
scala> val bar1 = Bar("true", "false")
bar1: Stuff.Thing = Thing(true,false)
It's no good for the rendering via toString, though, as you can see.
case class Thing(id: String, name: String)
type Foo = Thing
type Bar = Thing
if you say new Bar("a","b") it will work
Do you want Foo and Bar to be distinguishable (as when they are different case classes) or not? If yes, and you just want to avoid repeating list of fields, you can do this:
case class Foo(thing: Thing)
case class Bar(thing: Thing)
But this will obviously make them a bit less convenient to use in pattern matching or access fields. Field access can be improved a bit:
trait ThingWrapper {
def thing: Thing
def id = thing.id
def name = thing.name
}
case class Foo(thing: Thing) extends ThingWrapper
case class Bar(thing: Thing) extends ThingWrapper
{
class MyClass(name: String) {}
val x = new MyClass("x")
println(x.name) // Error name is not a member of MyClass
}
but
{
abstract class Base
case class MyClass(name: String) extends Base {}
var x = new MyClass("x")
println(x.name) // name is a member of MyClass
}
So, what's the deal with case classes? Why are all of the constructor parameters turned into variables.
name is member in both examples, but private in your first example while public in your second. Case classes make their constructor parameters public val by default.
Pattern matching is the most important but not the only application for case classes. Another important point is that they implement the equals and hashCode methods in terms of the constructor arguments (aka product elements). Therefore, case classes are very useful for defining data structures that serve as elements in sets or keys in maps. That in turn only makes sense if these elements are visible.
Compare:
class Foo(val i: Int)
val set1 = Set(new Foo(33))
set1.contains(new Foo(33)) // false!!
And:
case class Bar(val i: Int)
val set2 = Set(Bar(33)
set2.contains(Bar(33)) // true!
Two case class instances with equal parameters are equal themselves. You can imagine them representing some "constants". This implies that you should not have mutable state in them.
You can, however, use a second parameter list to exclude arguments from the equality:
case class Baz(i: Int)(val n: Long)
Baz(33)(5L) == Baz(33)(6L) // true!
Another useful feature which implies that the constructor arguments become values, is making copies. This is the way immutable data is changes—you create a new instance with a particular value changed, leaving the original value in place.
case class Person(name: String, age: Int)
val p1 = Person("Fuzzi", 33)
val p2 = p1.copy(age = 34)
The copy method uses default values for all unspecified argument, taking those values from the constructor args.
Just to be clear, the constructor arguments aren't used to create variables, they're used to create values.
If you specify val in your first example, the non-case class:
class MyClass(val name: String) {}
then you also get the argument translated into a public value, the same as is done for the case class.
In the example on the Scala-Lang site it says:
It makes only sense to define case classes if pattern matching is used
to decompose data structures. The following object defines a pretty
printer function for our lambda calculus representation:
followed by the example code:
object TermTest extends Application { def printTerm(term: Term) {
term match {
case Var(n) =>
print(n)
case Fun(x, b) =>
print("^" + x + ".")
printTerm(b)
case App(f, v) =>
Console.print("(")
printTerm(f)
print(" ")
printTerm(v)
print(")")
} } def isIdentityFun(term: Term): Boolean = term match {
case Fun(x, Var(y)) if x == y => true
case _ => false } val id = Fun("x", Var("x")) val t = Fun("x", Fun("y", App(Var("x"), Var("y")))) printTerm(t) println println(isIdentityFun(id)) println(isIdentityFun(t)) }
To add something to my comment due to lack of available space: consider the following example case class:
case class My(x: Int)
If you save it to file and pass it to scalac -print, you get following expanded code (I removed unimportant stuff):
case class My extends Object with Product with Serializable {
<caseaccessor> <paramaccessor> private[this] val x: Int = _;
<stable> <caseaccessor> <accessor> <paramaccessor> def x(): Int = My.this.x;
Notice <caseaccessor>s here.
And then companion object:
<synthetic> object My extends runtime.AbstractFunction1 with Serializable {
case <synthetic> def apply(x: Int): My = new My(x);
case <synthetic> def unapply(x$0: My): Option = if (x$0.==(null))
scala.this.None
else
new Some(scala.Int.box(x$0.x()));
case <synthetic> <bridge> def apply(v1: Object): Object = My.this.apply(scala.Int.unbox(v1));
//...
Notice apply and unapply here. If you look at complete output yourself, you'll learn more about how scala generates your code.
I'd like to produce JSON for a List that includes both base classes and derived classes. The code below only produces JSON for the Animal class (I do not get the breed field for the Dog type members). Some help would be appreciated.
import play.api.libs.json._
class Animal (val name:String) {
}
object Animal {
implicit object animalWrite extends Writes[Animal] {
def writes(ts: Animal) = JsObject(Seq("name" -> JsString(ts.name)))
}
}
case class Dog (override val name:String, val breed: String)
extends Animal(name) {
}
object Dog {
implicit val format = Json.format[Dog]
}
case class Cat (override val name:String, val hairLength: Int)
extends Animal(name) {
}
object Cat {
implicit val format = Json.format[Cat]
}
object helloWorld extends App {
// The list below outputs: [{"name":"Ruff","breed":"labrador"}]
// val l = List[Dog](Dog("Ruff", "labrador"))
// The list below outputs: [{"name":"Ruff"},{"name":"Fluffy"}]
// I expect to see: [{"name":"Ruff","breed":"labrador"},{"name":"Fluffy","hairLength":3}]
val l = List[Animal](Dog("Ruff", "labrador"), Cat("Fluffy", 3))
println(Json.toJson(l))
}
Scala and Play newbie here, please excuse inappropriate use of terminology.
The json API extensively uses implicit parameters which is a feature of Scala where you can provide an "implicit" parameter list and if you don't specify those parameters the compiler will try to find an object in the current scope that is marked as implicit and matches that signature.
So if you for example would write:
implicit val s = "my implicit string"
def magicPrint(implicit message: String) { println(message) }
// and then call it
magicPrint
The compiler would select s for the parameter message since it is in scope and has the correct type (String), so after the implicit resolution the last line of code would actually look more like this
magicPrint(s)
The Format/Writer is selected by the compiler, at compile time, with an implicit parameter. If you look at the signature of the method, toJson[A](item: A)(implicit writes: Writes[A]), it takes an implicit Writes[A] which in your case is a Writes[List[Animal]] since List[Animal] is the type of your list l. Play contains a default has a writer that takes care of the collection (DefaultWrites.traversableWrites) which in turn takes an implicit Writes[A] - in your case Writes[Animal], so the compiler will select and pass your animalWrites.
That your list contains different types of animals is something that happens at runtime and the compiler has no way of knowing that from the type information available at your Json.toJson(l)
So, as you see you cannot achieve what you want in the way you thought, but you can do it in almost the same way by letting the animal writer know about the subtypes, for example:
implicit object animalWrite extends Writes[Animal] {
def writes(ts: Animal) = ts match {
// this will get an implicit Writes[Dog] since d is a Dog
case d: Dog => Json.toJson(d)
// this will get an implicit Writes[Cat] since c is a Cat
case c: Cat => Json.toJson(c)
case x => throw new RuntimeException(s"Unknown animal $x")
}
}
Hope this helped!