Scala case class conversion - scala

Is there a way to convert one case class to another when they have the same fields and inherit from the same trait, without providing a converter function (that would simply do the one to one field mapping)?
For example:
trait UberSomething {
val name: String
}
// these may be located in different files
case class Something(name: String) extends UberSomething
case class SomethingOther(name: String) extends UberSomething
val s = Something("wtv")
//s.asInstanceOf[SomethingOther] FAILS

First of all never define trait members as val if they are meant to be implemented at a later point.
trait UberSomething {
def name: String
}
// these maybe in different files
case class Something(name: String) extends UberSomething
case class SomethingOther(name: String) extends UberSomething
import shapeless._, ops.hlist.Align
Another approach I've seen somewhere on Stackoverflow before, so apologies for stealing street cred, is to use Align such that order of the fields wouldn't matter.
class Convert[Target] {
def apply[Source, HLS <: HList, HLT <: HList](s: Source)(implicit
// Convert the Source to an HList type
// include field names, e.g "labelled"
// Shapeless "generates" this using an implicit macro
// it looks at our type, extracts a list of (Name, Type) pairs
genS: LabelledGeneric.Aux[Source, HLS],
// Convert the Target o an HList type
// include field names, e.g "labelled"
// So again we have a (Name, Type) list of pairs this time for Target
genT: LabelledGeneric.Aux[Target, HLT],
// Use an implicit align to make sure the two HLists
// contain the same set of (Name, Type) pairs in arbitrary order.
align: Align[HLS, HLT]
) = genT from align(genS to s)
}
// Small trick to guarantee conversion only requires
// a single type argument, otherwise we'd have to put something
// in place for HLS and HLT, which are meant to be path dependant
// and "calculated" by the LabelledGeneric.Repr macro so it wouldn't work as it breaches the "Aux pattern", which exposes a type member materialized by a macro in this case.
// HLT and HLS come from within genS.Repr and genT.Repr.
def convert[T] = new Convert[T]
This is a bit better as the HList params are nicely masked as part of apply so you don't trip yourself up.
val sample = Something("bla")
convert[SomethingOther](sample) // SomethingOther("bla")
Let's review this line: genT from align(genS to s).
First genS to s converts the Source instance to a LabelledGeneric, e.g an HList with field info.
Align aligns the types and fields of the created HList for the Source type to match the Target type.
genT from .. allows us to create an instance of Target from an HList granted the compiler can "prove" the fields and types are "all there", which is something we already have with Align.

You can do that using implicit conversions, eg:
trait UberSomething {
val name: String
}
case class Something(name: String) extends UberSomething
case class SomethingOther(name: String) extends UberSomething
object Something {
implicit def somethingToSomethingOther(s:Something):SomethingOther = SomethingOther(s.name)
}
object SomethingOther {
implicit def somethingOtherToSomething(s:SomethingOther):Something = Something(s.name)
}
val s = Something("wtv")
val so:SomethingOther = s

Related

How to define implicit Writes in trait

I have multiple case classes representing values in DB for ex User which saves user based properties like name / age / address and CallLog which saves timestamp / status_of_call
What i want to achieve
I want to have a helper function which accepts list of models and checks if the list is empty then returns "error" otherwise should return json array of the list.
My Approach
I want to have a trait which groups certain models in it and the helper method will accept either the trait or List of it in order to check or may be have a generic which implements the trait.
Problem
Since implicit writes are tightly coupled with the model class, compiler throws the error on the line Json.toJson(list)
Things i have tried
Kept implicit in trait and got recursive type error
I am scala noob pardon me if this sounds silly
Thanks in advance
Since User, CallLog, etc. will be serialized differently, Each Writes[T] will be different for each implementation of your Model trait, so a Writes[Model] has to know about the implementation it is trying to serialize.
It is therefore not possible to have it part of the Model trait, because this information isn't known yet when you define it.
A workaround in your case would be to define your Writes[Model] in the scope of your helper function instead.
An implementation of your helper function could be like this :
import play.api.libs.json.{JsValue, Json, Writes}
sealed trait Model
case class User(name: String, age: String, address: String) extends Model
object User {
implicit val userWrites = Json.writes[User]
}
case class CallLog(timestamp: String, status_of_call: String) extends Model
object CallLog {
implicit val callLogWrites = Json.writes[CallLog]
}
implicit val modelWrites = new Writes[Model] {
override def writes(o: Model): JsValue = o match {
case u: User => Json.toJson(u)
case cl: CallLog => Json.toJson(cl)
}
}
def helper(models: Model*): Either[JsValue, String] = models match {
case Nil => Right("Error")
case _ => Left(Json.toJson(models))
}
helper(User("John", "32", "..."))
helper(User("John", "32", "..."), CallLog("now", "In progress"))

Method parameter of dependent type of another implicit method parameter - is it possible?

(A longish) illustration:
// Base trait for different domains. Domains hold value-like things,
// which have some known relationships between them.
trait Domain {
type Data
type IntData <: Data
type PairData <: Data
def select(query: Query, data: Data): Data = ???
}
// Base trait for queries. Queries operate in a
// specific (but arbitrary) domain.
sealed trait Query {
def select[D <: Domain](domain: D)(data: domain.Data): domain.Data =
domain.select(this, data)
}
// Specific queries
case object IdQuery extends Query
case class SuccQuery(val q: Query) extends Query
// ...
// Specific domains
object Values extends Domain {
sealed abstract class Data
case class IntData(val data: Int) extends Data
case class PairData(val a: Data, val b: Data) extends Data
}
object Paths extends Domain {
// ...
}
object Test {
def test {
val query: Query = ???
val value: Values.Data = ???
val path: Paths.Data = ???
val valueResult = query.select(Values)(value)
val pathResult = query.select(Paths)(path)
}
}
This is a complete working code. In the example, I have a fixed case-hierarchy of structured queries which need to operate on different domains somehow. The common parts of the domains are part of the Domain trait.
Let's look at the select definition in the Query trait: it takes a specific domain (which should be a stable value), and data of a dependent type of this domain. The client passes both the specific domain and the data to the select method. This is an example of a method in which a parameter is of a dependent type of another (non-implicit) parameter.
I want to somehow "hide" the domain, so that only the data is passed (the domain being passed implicitly). But I can't quite nail it:
If I declare:
def select[D <: Domain](implicit domain: D)(data: domain.Data)
the compiler complains because implicit method parameters should come last.
If I swap the two parameters:
def select[D <: Domain](data: domain.Data)(implicit domain: D)
the compiler complains about domain being defined after being used:
illegal dependent method type: parameter appears in the type of another parameter in the same section or an earlier one
What can I do to make the domain parameter be passed implicitly?
More generally, is it possible to have a method parameter of a dependent type of another implicit method parameter?
I would suggest this workaround:
def select[D <: Domain](implicit domain: D) =
new Selector[D](domain)
class Selector[D <: Domain](domain: D) {
def data(data: domain.Data): domain.Data = ???
}
select.data(someData)

Chaining path-dependent types and instantiating them when they having different parameter lists in Scala

I'm experimenting with writing more statically type-safe code by implementing a simple card game. In this game, there are several unique cards and each card has a card-specific effect which may require additional parameters (e.g., a target for the effect). A player holds two cards and on their turn chooses to play one of them, causing that card's effect to take place.
Note: most of the details in this post are from trying it out in the REPL. I have a less statically type-safe implementation written but I want to make sure that what I want is feasible before diving completely into it.
Here are some relevant definitions:
trait CardEffectParams
case class OneTarget(player: Player) extends CardEffectParams
case class TwoTargets(player1: Player, player2: Player) extends CardEffectParams
// ...
trait Card {
// the parameters to use are specific to the card
type Params <: CardEffectParams
}
trait Hand {
case class CardInHand(card: Card) { /* with ctor not accessible from outside */ }
// a player can hold two cards
val card1: CardInHand
val card2: CardInHand
}
I want to delegate the choosing of which card to play to some strategy so I can see how different strategies compare. This is where I'm stuck: I want to limit the cards you can return to the ones in the Hand object passed in the parameters, which I can do by typing it as hand.CardInHand:
trait Strategy {
def choose(hand: Hand, gameState: GameState): hand.CardsInHand
}
But I also want to pass extra parameters: for example, one card might allow me to target just one player (e.g., skip their turn), but another might let me target two (e.g., swap their cards). These are modelled by CardEffectParams. So I want to return both hand.CardsInHand and a cardInHand.card.Params where cardInHand is the instance I'm returning, something like this:
/* NOT valid scala */
trait Strategy {
def choose(hand: Hand, gameState: GameState): (c: hand.CardsInHand, c.card.Params)
}
So the first question is, can this be done? How would you represent this relationship?
I'm also stuck on how to instantiate the CardEffectParams subclasses, since each one may have different parameter lists. My first thought is to do a pattern match, but this fails because the type of the match block is the common ancestor of all possible results:
case object CardA extends Card {
type Params = OneTarget
}
case object CardB extends Card {
type Params = TwoTargets
}
object RandomStrategy extends Strategy {
def choose(hand: Hand, gameState: GameState) = {
val card: Card = /* randomly pick card1 or card2 */
/* the type of the match block is CardEffectParams, not card.Params */
val param: card.Params = card match {
case CardA => OneTarget(...)
case CardB => TwoTargets(...)
}
}
}
My current idea is to define a factory method within each card object that takes an hlist of arguments from which it produces the correct type:
trait Card {
type Params <: CardEffectParams
type HListTypeOfParams = /* insert shapeless magic */
def create[L <: HListTypeOfParams](l: L): Params
}
from which I can then do the following?
// no idea if this works or not
val card: Card = ...
val params: card.Params = card match {
case c: CardA => c.create(1 :: HNil)
case c: CardB => c.create(1 :: 2 :: HNil)
}
But I feel like I've gone too far down the rabbit hole. Is what I want to achieve possible? Is it necessary? Do I need to dive so deep into typing to ensure static type safety or am I missing something really elementary?
For the first question, I would replace your tuple with a type that represents the relationship
trait CardAndParams {
type C <: Card
val card: C
val params: C#Params
}
def choose[R <: CardAndParams](hand: Hand, gameState: GameState)(
implicit helper: Helper {type Out = R}): R
You will need to use implicits like my Helper example to drive the actual strategy implementations and ensure the correct R is inferred. This is also the more usual way to do type-level computation:
sealed trait RandomStrategyHelper[C <: Card] {
def params(): C#Params
}
object RandomStrategyHelper {
implicit def forCardA = new RandomStrategyHelper[CardA] {
def params() = 1 :: HNil
}
implicit def forCardB = new RandomStrategyHelper[CardB] {
def params() = 1 :: 2 :: HNil
}
}
def randomParams[C <: Card](card: C)(implicit rsh: RandomStrategyHelper[C]) =
rsh.params()
But I guess you need a way to move from your randomly-generated card to a strongly typed one, and for that the pattern match seems appropriate, since it would be difficult to represent a random card at type level.
In general this kind of type-level programming is possible but hard in Scala - the language wasn't really designed for it. If you want to push this as far as it will go you may be better off using something like Idris.

Matching against subclasses in macro

I need to convert a string value into an actual type, so i decided to try a macro-way to do this. I have a bunch of data types:
sealed abstract class Tag(val name: String)
case object Case1 extends Tag("case1")
case object Case2 extends Tag("case2")
case object Case3 extends Tag("case3")
etc...
I want to write a simple resolver:
val tag: Tag = TagResolver.fromString("case2")
This line should return Case2 respectively. I manager to do the following:
def typeFromString(c: Context)(name: c.Expr[String]): c.Expr[Tag] = {
import c.universe._
val tag = typeTag[Tag]
val accSymb = tag.tpe.typeSymbol.asClass
val subclasses = accSymb.knownDirectSubclasses // all my cases
subclasses.map { sub =>
val name = sub.typeSignature.member(newTermName("name")).asMethod // name field
???
}
}
But how can i match name: c.Expr[String] against value of name field and if matched return the appropriate tag?
I don't think there's reliable way of doing this, because knownDirectSubclasses can refer to classes that haven't been compiled yet, so we can't evaluate them.
If you can put these values as annotations on the classes, then these annotations can be read even when classes are being compiled in the current compilation run (via the Symbol.annotations API). Please note, however, that knownDirectSubclasses has known issues: https://issues.scala-lang.org/browse/SI-7046.

Custom Scala enum, most elegant version searched

For a project of mine I have implemented a Enum based upon
trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
}
from Case objects vs Enumerations in Scala. I worked quite nice, till I run into the following problem. Case objects seem to be lazy and if I use Currency.value I might actually get an empty List. It would have been possible to make a call against all Enum Values on startup so that the value list would be populated, but that would be kind of defeating the point.
So I ventured into the dark and unknown places of scala reflection and came up with this solution, based upon the following SO answers. Can I get a compile-time list of all of the case objects which derive from a sealed parent in Scala?
and How can I get the actual object referred to by Scala 2.10 reflection?
import scala.reflect.runtime.universe._
abstract class Enum[A: TypeTag] {
trait Value
private def sealedDescendants: Option[Set[Symbol]] = {
val symbol = typeOf[A].typeSymbol
val internal = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol]
if (internal.isSealed)
Some(internal.sealedDescendants.map(_.asInstanceOf[Symbol]) - symbol)
else None
}
def values = (sealedDescendants getOrElse Set.empty).map(
symbol => symbol.owner.typeSignature.member(symbol.name.toTermName)).map(
module => reflect.runtime.currentMirror.reflectModule(module.asModule).instance).map(
obj => obj.asInstanceOf[A]
)
}
The amazing part of this is that it actually works, but it is ugly as hell and I would be interested if it would be possible to make this simpler and more elegant and to get rid of the asInstanceOf calls.
Here is a simple macro based implementation:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
abstract class Enum[E] {
def values: Seq[E] = macro Enum.caseObjectsSeqImpl[E]
}
object Enum {
def caseObjectsSeqImpl[A: c.WeakTypeTag](c: blackbox.Context) = {
import c.universe._
val typeSymbol = weakTypeOf[A].typeSymbol.asClass
require(typeSymbol.isSealed)
val subclasses = typeSymbol.knownDirectSubclasses
.filter(_.asClass.isCaseClass)
.map(s => Ident(s.companion))
.toList
val seqTSymbol = weakTypeOf[Seq[A]].typeSymbol.companion
c.Expr(Apply(Ident(seqTSymbol), subclasses))
}
}
With this you could then write:
sealed trait Currency
object Currency extends Enum[Currency] {
case object USD extends Currency
case object EUR extends Currency
}
so then
Currency.values == Seq(Currency.USD, Currency.EUR)
Since it's a macro, the Seq(Currency.USD, Currency.EUR) is generated at compile time, rather than runtime. Note, though, that since it's a macro, the definition of the class Enum must be in a separate project from where it is used (i.e. the concrete subclasses of Enum like Currency). This is a relatively simple implementation; you could do more complicated things like traverse multilevel class hierarchies to find more case objects at the cost of greater complexity, but hopefully this will get you started.
A late answer, but anyways...
As wallnuss said, knownDirectSubclasses is unreliable as of writing and has been for quite some time.
I created a small lib called Enumeratum (https://github.com/lloydmeta/enumeratum) that allows you to use case objects as enums in a similar way, but doesn't use knownDirectSubclasses and instead looks at the body that encloses the method call to find subclasses. It has proved to be reliable thus far.
The article "“You don’t need a macro” Except when you do" by Max Afonov
maxaf describes a nice way to use macro for defining enums.
The end-result of that implementation is visible in github.com/maxaf/numerato
Simply create a plain class, annotate it with #enum, and use the familiar val ... = Value declaration to define a few enum values.
The #enum annotation invokes a macro, which will:
Replace your Status class with a sealed Status class suitable for acting as a base type for enum values. Specifically, it'll grow a (val index: Int, val name: String) constructor. These parameters will be supplied by the macro, so you don't have to worry about it.
Generate a Status companion object, which will contain most of the pieces that now make Status an enumeration. This includes a values: List[Status], plus lookup methods.
Give the above Status enum, here's what the generated code looks like:
scala> #enum(debug = true) class Status {
| val Enabled, Disabled = Value
| }
{
sealed abstract class Status(val index: Int, val name: String)(implicit sealant: Status.Sealant);
object Status {
#scala.annotation.implicitNotFound(msg = "Enum types annotated with ".+("#enum can not be extended directly. To add another value to the enum, ").+("please adjust your `def ... = Value` declaration.")) sealed abstract protected class Sealant;
implicit protected object Sealant extends Sealant;
case object Enabled extends Status(0, "Enabled") with scala.Product with scala.Serializable;
case object Disabled extends Status(1, "Disabled") with scala.Product with scala.Serializable;
val values: List[Status] = List(Enabled, Disabled);
val fromIndex: _root_.scala.Function1[Int, Status] = Map(Enabled.index.->(Enabled), Disabled.index.->(Disabled));
val fromName: _root_.scala.Function1[String, Status] = Map(Enabled.name.->(Enabled), Disabled.name.->(Disabled));
def switch[A](pf: PartialFunction[Status, A]): _root_.scala.Function1[Status, A] = macro numerato.SwitchMacros.switch_impl[Status, A]
};
()
}
defined class Status
defined object Status