Get name of property implicit method was called on - scala

Using Scala, I want to achieve the following:
// SETUP:
implicit class ExampleOps(s: String) {
def name: String = ???
}
case class Example(prop1: String, prop2: String)
val e = Example("a", "b")
// BEHAVIOR I WANT:
e.prop1.name // should return "prop1"
In this case def name somehow knows what it's being called on. How is this accomplished?

As I explained in the comment, macros could be used to a certain extent. However, they can not magically restore some information that is lost. For example if you store the property value in an arbitrary variable, there is no way to figure out from which property it was read:
val x: String = e.prop1
x.name // no way to know if came from calling e.prop1
So I think the only solution is to use a custom type. You could allow them to be unrolled as strings implicitly:
object Property {
implicit def value[A](prop: Property[A]): A = prop.value
}
case class Property[+A](name: String, value: A)
case class Example(value1: String, value2: String) {
def prop1 = Property("prop1", value1)
def prop2 = Property("prop2", value2)
}
val e = Example("a", "b")
e.prop1.toUpperCase
e.prop1.name

Related

Changing implicit value in scala response of nested object

I have a controller
def getCars(notation: Option[Boolean] = Some(false)) = identified.auth(hasOceanScope).async { implicit request =>
carService.getCars().map {
case Seq() => Response.NotFound
case cars => Response.Ok(cars)
}
}
Car case class looks like this:
case class Car(
name: String,
createdAt: LocalDateTimeOffset,
wheels: Seq[Wheel]
)
object Car{
implicit val wheelFormat = Wheel.format
implicit def toOffset(date: LocalDateTime): LocalDateTimeOffset = LocalDateTimeOffset.apply(date)
implicit val format = Json.format[Car]
case class Wheel(
name: String,
createdAt: LocalDateTimeOffset
)
object Wheel{
implicit val format = Json.format[Wheel]
implicit def toOffset(date: LocalDateTime): LocalDateTimeWithOffset = LocalDateTimeWithOffset.apply(date)
)
When notation query parameter is true -> want to return createdAt Car object and Wheel object field with notation => 2022-10-22T00:00:00#1
When notation query parameter is false -> want to return createdAt Car object and Wheel object field without notation => 2022-10-22T00:00:00
That is why I have create two formats in LocalDateTimeOffset object
case class LocalDateTimeWithOffset(dt: LocalDateTime, offset: Int) {
val localDateTimeWithOffsetReads: Reads[LocalDateTimeWithOffset] = Reads.of[String].flatMap {
str => if (str.contains("#")) {
val (dt, offset) = str.splitAt(str.indexOf("#"))
Reads.pure(LocalDateTimeWithOffset(LocalDateTime.parse(dt), offset.drop(1).toInt))
} else {
Reads.pure(LocalDateTimeWithOffset(LocalDateTime.parse(str), 1))
}
}
val localDateTimeWithOffsetWrites: Writes[LocalDateTimeWithOffset] = new Writes[LocalDateTimeWithOffset] {
override def writes(a: LocalDateTimeWithOffset): JsValue = JsString(a.dt.format(dateTimeUTCFormatter) + s"#${a.offset}")
}
val localDateTimeWithOffsetWritesOff: Writes[LocalDateTimeWithOffset] = new Writes[LocalDateTimeWithOffset] {
override def writes(a: LocalDateTimeWithOffset): JsValue = JsString(a.dt.format(dateTimeUTCFormatter))
}
val localDateTimeWithoutOffsetFormat: Format[LocalDateTimeWithOffset] = Format(localDateTimeWithOffsetReads, localDateTimeWithOffsetWritesOff)
val localDateTimeWithOffsetFormat: Format[LocalDateTimeWithOffset] = Format(localDateTimeWithOffsetReads, localDateTimeWithOffsetWrites)
implicit var format: Format[LocalDateTimeWithOffset] = localDateTimeWithoutOffsetFormat
}
But how can I use two different formats from controller based on notation query parameter value?
Well just looking at your question's title, changing implicit value is not something you would see Scala developers do, because compiler is responsible to lookup for implicit values, and you would definitely want to avoid ambiguous implicits found error. instead, you see developers using something so called type class instance constructor or something similar. This is how it works in your case:
Assuming you have a class A, which can be formatted to/from Json in many ways:
case class A(field1: String) // could have more fields
object A {
val formatFirstApproach: Format[A] = ???
val formatSecondApproach: Format[A] = ???
// note that the above instances are not implicit
def getFormat(somePredicate: Boolean): Format[A] = {
// input parameters can be anything, these are the parameters you need,
// in order to be able to decide which instance to return
if (somePredicate) formatFirstApproach else formatSecondApproach
}
}
And then, given a class B which has an instance variable of type A in it, you can use the type class instance constructor:
case class B(a: A, field2: Int)
object B {
// this is the type class instance constructor, since it constructs an instance of a type class (Format in this case)
implicit def format(implicit aFormatter: Format[A]): Format[B] = Json.format
}
And the thing is, you probably would not care about the serialization unless in the controller layer, so in the controller layer you can do:
def someApi(flag: Boolean) = Action async { req =>
implicit val aFormatter = A.getFormat(flag) // that's it, you don't need to mention anything more anywhere
businessLogic().map {
case Seq() => Response.NotFound
case cars => Response.Ok(Json.toJson(cars))
}
}

Can I avoid using structural typing in this scenario?

I have some code that uses both a third-party library and my own library. In my own library, I don't want to have a dependency on the third-party one so I want one of my methods to accept a more generic type as a parameter. Unfortunately, I cannot extend or mixin a trait to the 3rd party classes since they are generated using factory methods & the classes are final.
I can get around this issue by using structural typing but I was wondering if there is an alternative? I don't want to have to iterate through each record returned by the factory method and "new up" instances of a separate type if possible.
I've boiled it down to a scenario like the following:
Third-party library code that cannot be changed
// Class inside library cannot be extended due to it being 'final'
final class SpecificRecord(val values: IndexedSeq[String]) {
def get(i: Int): String = {
values(i)
}
}
// A companion object simply to create some sample data in an iterator
object SpecificRecord{
def generateSpecificRecords(): Iterator[SpecificRecord] = new Iterator[SpecificRecord] {
var pointerLocation: Int = 0
private val state = IndexedSeq(
IndexedSeq("Row1 Col1", "Row1 Col2", "Row 1 Col3"),
IndexedSeq("Row2 Col1", "Row2 Col2", "Row 2 Col3")
)
override def hasNext: Boolean = {
if (pointerLocation < state.length) true else false
}
override def next(): SpecificRecord = {
val record = new SpecificRecord(state(pointerLocation))
pointerLocation += 1
record
}
}
}
As you can see above, the SpecificRecord class is final and the specificRecords val is an Iterator with a bunch of SpecificRecord in it. I don't want to have to iterate through each specificRecord and create a new, more generic, object if possible.
My code that can be changed
val specificRecords: Iterator[SpecificRecord] = SpecificRecord.generateSpecificRecords()
type gettable = {
def get(i: Int): String
}
def printRecord(records: Iterator[gettable]): Unit = {
for (record <- records) {
println(record.get(0), record.get(1), record.get(2))
}
}
printRecord(specificRecords)
This correctly prints:
(Row1 Col1,Row1 Col2,Row 1 Col3)
(Row2 Col1,Row2 Col2,Row 2 Col3)
I have a printRecord method that doesn't really care what type is passed in, as long as it has a method like get(Int): String. This a pretty decent solution but I was wondering if it would be possible to do this without structural typing?
This is a typical use case for type classes.
trait Gettable[T] {
def get(t: T, i: Int): String
}
object Gettable {
implicit object SpecificRecordGettable extends Gettable[SpecificRecord] {
def get(sr: SpecificRecord, i: Int) = sr.get(i)
}
}
def printRecord[T : Gettable](records: Iterator[T]) = {
val getter = implicitly[Gettable[T]]
records.foreach { record =>
println(getter.get(record, 0), getter.get(record, 1), getter.get(record, 2))
}
}
This is a bit more verbose than your approach with structured types: for each type you want to be gettable, you have to add an implicit object implementing the get, but it works without reflection, which is a good thing.
Another advantage of this approach is its flexibility: the underlying type does not have to have get specifically, you can implement anything with the implicit. E.g.:
implicit object ArrayGettable extends Gettable[Array[String]] {
def get(a: Array[String], i: Int) = a(i)
}
implicit object ProductGettable extends Gettable[Product] {
def get(p: Product, i: Int) = p.productIterator.drop(i).next.toString
}
Now, your printRecord works with string arrays too (as long as they have at least three elements), and even tuples and case classes.
Try this:
printRecord[Product](Iterator((1,2, "three"), ("foo", "bar", 5)))
Or this:
case class Foo(x: String, y: Int, z: Seq[Int])
printRecord[Product](Iterator(Foo("bar", 1, 1::2::Nil), ("foo", "bar", "baz")))
A similar but a little bit less verbose approach is to just define an implicit 'getter' without bothering with type classes:
def printRecord[T](records: Iterator[T])(implicit getter: (T,Int) => String) =
records.foreach { record =>
println(getter(record, 0), getter(record, 1), getter(record, 2))
}
object Getters {
implicit def getter(sr: SpecificRecord, i: Int) = sr.get(i)
implicit def getter(a: Array[String], i: Int) = a(i)
implicit def getter(p: Product, i: Int) = p.productIterator.drop(i).next.toString
}
This is fairly equivalent in usage, the difference being that type class lets you potentially define more than one method, but if you only ever need get, then this would save you a few keystrokes.

Scala ClassCastException on Option.orNull

When I try to run following code:
def config[T](key: String): Option[T] = {
//in reality this is a map of various instance types as values
Some("string".asInstanceOf[T])
}
config("path").orNull
I'm getting error:
java.lang.String cannot be cast to scala.runtime.Null$
java.lang.ClassCastException
Following attempts are working fine:
config[String]("path").orNull
config("path").getOrElse("")
Since getOrElse works its confusing why null is so special and throws an error. Is there a way for orNull to work without specifying generic type ?
scalaVersion := "2.12.8"
Just to show how you may avoid the use asInstanceOf to get the values from a typed config.
sealed trait Value extends Product with Serializable
final case class IntValue(value: Int) extends Value
final case class StringValue(value: String) extends Value
final case class BooleanValue(value: Boolean) extends Value
type Config = Map[String, Value]
sealed trait ValueExtractor[T] {
def extract(config: Config)(fieldName: String): Option[T]
}
object ValueExtractor {
implicit final val IntExtractor: ValueExtractor[Int] =
new ValueExtractor[Int] {
override def extract(config: Config)(fieldName: String): Option[Int] =
config.get(fieldName).collect {
case IntValue(value) => value
}
}
implicit final val StringExtractor: ValueExtractor[String] =
new ValueExtractor[String] {
override def extract(config: Config)(fieldName: String): Option[String] =
config.get(fieldName).collect {
case StringValue(value) => value
}
}
implicit final val BooleanExtractor: ValueExtractor[Boolean] =
new ValueExtractor[Boolean] {
override def extract(config: Config)(fieldName: String): Option[Boolean] =
config.get(fieldName).collect {
case BooleanValue(value) => value
}
}
}
implicit class ConfigOps(val config: Config) extends AnyVal {
def getAs[T](fieldName: String)(default: => T)
(implicit extractor: ValueExtractor[T]): T =
extractor.extract(config)(fieldName).getOrElse(default)
}
Then, you can use it like this.
val config = Map("a" -> IntValue(10), "b" -> StringValue("Hey"), "d" -> BooleanValue(true))
config.getAs[Int](fieldName = "a")(default = 0) // res: Int = 10
config.getAs[Int](fieldName = "b")(default = 0) // res: Int = 0
config.getAs[Boolean](fieldName = "c")(default = false) // res: Boolean = false
Now, the problem becomes how to create the typed config from a raw source.
And even better, how to directly map the config to a case class.
But, those are more complex, and probably is better to just use something already done, like pureconfig.
Just as an academic exercise, lets see if we can support Lists & Maps.
Lets start with lists, a naive approach would be to have another case class for values which are lists, and create a factory of extractors for every kind of list (this process is formally know as implicit derivation).
import scala.reflect.ClassTag
final case class ListValue[T](value: List[T]) extends Value
...
// Note that, it has to be a def, since it is not only one implicit.
// But, rather a factory of implicits.
// Also note that, it needs another implicit parameter to construct the specific implicit.
// In this case, it needs a ClasTag for the inner type of the list to extract.
implicit final def listExtractor[T: ClassTag]: ValueExtractor[List[T]] =
new ValueExtractor[List[T]] {
override def extract(config: Config)(fieldName: String): Option[List[T]] =
config.get(fieldName).collect {
case ListValue(value) => value.collect {
// This works as a safe caster, which will remove all value that couldn't been casted.
case t: T => t
}
}
}
Now, you can use it like this.
val config = Map("l" ->ListValue(List(1, 2, 3)))
config.getAs[List[Int]](fieldName = "l")(default = List.empty)
// res: List[Int] = List(1, 2, 3)
config.getAs[List[String]](fieldName = "l")(default = List("Hey"))
// res: String = List() - The default is not used, since the field is a List...
// whose no element could be casted to String.
However, this approach is limited to plain types, if you need a List of other generic type, like a List of Lists. Then, this won't work.
val config = Map("l" ->ListValue(List(List(1, 2), List(3))))
val l = config.getAs[List[List[String]]](fieldName = "l")(default = List.empty)
// l: List[List[String]] = List(List(1, 2), List(3)) ???!!!
l.head
// res: List[String] = List(1, 2)
l.head.head
// java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
The problem here is type erasure, which ClassTags can not solve, you may try to use TypeTags which can preserve the complete type, but the solution becomes more cumbersome.
For Maps the solution is quite similar, especially if you fix the key type to String (assuming what you really want is a nested config). But, this post is too long now, so I would leave it as an exercise for the reader.
Nevertheless, as already said, this can be broken easily, and is not completely robust.
There are better approaches, but I myself am not very skilled on those (yet), and even if I would be, the answer would be more long and really not necessary at all.
Lucky for you, even if pureconfig does not support YAML directly, there is a module which does, pureconfig-yaml.
I would suggest you to take a look to the module, and if you have further problems ask a new question tagging pureconfig and yaml directly. Also, if it is just a small doubt, you may try asking in thegitter channel.

How to extend String to add new unapply function for using it in extracting?

I want to use a Scala extractor to match my custom type with a String (in that specific order, not String to Scala). Here is my code:
class Scala
object Scala {
def apply(s: String) = new Scala
def unapply(sc: Scala) = Some("Scala")
}
class ExtendedString(s: String) {
def unapply(sc: Scala): Option[String] = {
Some(s)
}
}
implicit def str2Scala(s: String): ExtendedString = {
new ExtendedString(s)
}
Scala match {
case "abc" => println("Aha!")
case "def" => println("Yeah!")
case "scala" => println("Scala!")
}
But it does not work. I get an error:
Error:(20, 9) type mismatch;
found : String("abc")
required: A$A109.this.Scala.type
case "abc" => println("Aha!")
^
How can I fix it to make extractor work?
PS
My original idea was to provide an implicit converter to extend String class with ExtendedString class and then to implement unapply method there to make extraction possible.
There should be no need whatsoever for implicits if all you want is an extractor.
To define an extractor, you make an object with an unapply method. E.g.
object MyExtractor {
def unapply(value: ValueBeingMatched): Option[ExtractedValue] = { ... }
}
Then you match a value using the extractor
val myValue: ValueBeingMatched = ...
myValue match {
case MyExtractor(extractedValue) => println(s"I got $extractedValue!")
}
If you want your extractor to come back with multiple values, the unapply should return an option of tuple:
object MyExtractor {
def unapply(value: ValueBeingMatched): Option[(ResultType1, ResultType2, ...)] = { ... }
}
val myValue: ValueBeingMatched = ...
myValue match {
case MyExtractor(result1, result2, ...) => ...
}
I'm not clear on what you are trying to accomplish from your example code, so I'll make an example that maybe is relevant for you.
Let's say you make a custom Point class:
case class Point(x: Int, y: Int)
And you want to be able to extract points from a String. In this case, the ValueBeingMatched is a String, and the ExtractedValue is a Point. I'll also define the extractor in the Point object. As for functionality, let's assume that a string like "12,8" corresponds to a Point(12, 8).
object Point {
def unapply(s: String): Option[Point] = {
val parts = s.split(",")
// this is just example code, so I won't handle failures,
// but if it did fail, you'd return a None instead of Some
Some(Point(parts(0).toInt, parts(1).toInt))
}
}
Now that it's defined, you can match strings using the Point extractor:
val s = "3,4"
s match {
case Point(p) => // p is actually an instance of Point
}
edit to respond to feedback:
In order to match directly to a string, the value being matched must already be a String. So one way would be to add a converter method e.g.
instanceOfMyType.convertToString match {
case "abc" => println("Aha!")
}
Or you would have to write an extractor to allow
instanceOfMyType match {
case Extracted("abc") => println("Aha!")
}

How to set instance members from constructor in Scala?

In Scala I need to do something like following code in Java does:
public class A {
private String text;
public A(String text) {
this.text = text;
}
}
How can achieve that in Scala?
I know that I can use class A(text: String) { ... }, but this is only a very simplified example and not real case.
I have tried the following and it prints always null:
class A {
var text: String = null
def this(text: String) = {
this()
this.text = text
}
println(text)
}
Thanks for help.
In Scala classes have only one main constructor, and it's exactly what you define like this: class A(text: String) { ... }. All other constructors should call it at first and then do their own stuff.
All expressions in the body of the class are treated as body of the main constructor. So println(text) in you case is in the body of the main constructor. You call this() in your def this(text: String) constructor at first, so println is executed and only then you initialize text field.
From my point of view, main constructor can satisfy most of the real-life use-cases (especially with default and named arguments). So can you please elaborate on this and explain why you need the second one? Maybe we can find beter solution for the problem you are facing there.
Update regarding the comment
If you want to provide a set of optional arguments during construction time, then I recommend you to use Option instead of null values. So you can implement your A class like this:
class A(val a: Option[String] = None, val b: Option[Int] = None, c: Option[Double] = Some(2.0))
new A(b = Some(10))
All fields are now constant and have some default, but you can customize some of them. It's also possible that some combination of arguments are mutually exclusive, according to some business logic, for example. In this case it's reasonable to use several constructors. But even better solution can be to create several factory methods in companion object of the class and make constructor private. For example, if users of the class are allowed to provide either a and b or c, then you can write something like this:
class A private (val a: Option[String] = None, val b: Option[Int] = None, c: Option[Double] = Some(2.0))
object A {
def apply(a: String, b: Int) = new A(Some(a), Some(b))
def apply(c: Double) = new A(c = Some(c))
}
A("test", 1)
A(11.1)
class A {
var text: String = null
def this(text: String) = {
this()
this.text = text
println(text)
}
}
or
class A(text:String=""){
println(text)
}
or
class A(text:String=null){
println(text)
}
The correct way to do that is :
class A( text : String) {
def this() = this("") // or null
}
As Tenshi said, the default constructor is the A(text : String) and all other constructors, for instance this() must call it first.