Complex Sum Type in Scala - scala

I have the following Type that i need to model:
sealed trait FieldType
case object INT extends FieldType
case object UINT extends FieldType
case object FLOAT extends FieldType
case object DOUBLE extends FieldType
case object BOOL extends FieldType
case object STRING extends FieldType
case object DATETIME extends FieldType
case class LIST(fieldType: FieldType) extends FieldType
case class SET(fieldType: FieldType) extends FieldType
The Issue i have is that in fact LIST and SET are actually ComplexFieldType that contain FieldType. In other words LIST Can Not Contain List or SET, same thing for SET.
What would be the proper way to model that, to ensure exhaustivity issue i.e. having the compiler telling me when i am missing some values.
I tried to introduce intermediary sealed trait as in SimpleFieldType and ComplexFieldType but then the pattern matching was messed up.
Only solution that i see if I don't want to compound things as in
case object LIST_INT
is to use smart constructor for LIST and SET.
However I thought drop a note and see what the scalaSphere knows about it.

It works indeed as such:
sealed trait FieldType
sealed trait SimpleFieldType extends FieldType
case object INT extends SimpleFieldType
case object UINT extends SimpleFieldType
case object FLOAT extends SimpleFieldType
case object DOUBLE extends SimpleFieldType
case object BOOL extends SimpleFieldType
case object STRING extends SimpleFieldType
case object DATETIME extends SimpleFieldType
sealed trait ComplexFieldType extends FieldType
case class LIST(fieldType: SimpleFieldType) extends ComplexFieldType
case class SET(fieldType: SimpleFieldType) extends ComplexFieldType
val field1: FieldType = INT
field1 match {
case FLOAT =>
case BOOL =>
case INT =>
case UINT =>
case DATETIME =>
case DOUBLE =>
case STRING =>
case LIST(_) =>
case SET(_) =>
}
ScalaC detect the non-exhaustiveness. I just had to use https://github.com/rtimush/sbt-rewarn#sbt-rewarn as the recompilation just skip it

Related

Combine related ADTs in a type safe way

I'm designing a typesafe api to work with a "types" -- an abstraction in the application I'm working on. Here is how it looks like:
sealed trait EnumType
case object A extends EnumType
case object B extends EnumType
case object C extends EnumType
sealed abstract class TypeInfo[T <: EnumType](val enumType: T)
case class Ainfo() extends TypeInfo(A)
case class Binfo() extends TypeInfo(B)
case class Cinfo() extends TypeInfo(C)
sealed trait TypeMeta[T <: EnumType]
case class Ameta() extends TypeMeta[A.type]
case class Bmeta() extends TypeMeta[B.type]
case class Cmeta() extends TypeMeta[C.type]
case class TypeDescription[T <: EnumType](info: TypeInfo[T], meta: TypeMeta[T])
I'm confused about defining a function which would accept a List of TypeInfo and return TypeDescription. I currently implemeted it as follows:
//Type parameter with omitted bound? Is that type safe?
def toDescription(lst: List[TypeInfo[_]]): List[TypeDescription[_]] = {
lst map {
case a: Ainfo => TypeDescription(a, Ameta())
case b: Binfo => TypeDescription(b, Bmeta())
case c: Cinfo => TypeDescription(c, Cmeta())
}
}
To workaround the issue I used [_] pattern which does not look typesafely. Is there a way to redeclare the function?
Its type safe , however the 2 type parameters are bound individually to their own constraints and not to one another.
If you are looking to do that , I think you would need to define a method type parameter like so
//Type parameter with omitted bound? Is that type safe?
def toDescription[T<:EnumType](lst: List[TypeInfo[T]]): List[TypeDescription[T]] = {
lst map {
case a: Ainfo => TypeDescription(a, Ameta())
case b: Binfo => TypeDescription(b, Bmeta())
case c: Cinfo => TypeDescription(c, Cmeta())
}
}
Now if you tried to write
case a: Ainfo => TypeDescription(a, Bmeta())
you will get a compilation error

Exhaustive match on possible object values

First the code:
object MyEnums {
sealed abstract class MyEnum(val value: String)
case object First extends MyEnum("Some_ugly_looking_value1")
case object Second extends MyEnum("Some_ugly_looking_value2")
case object Third extends MyEnum("Some_ugly_looking_value3")
case object Fourth extends MyEnum("Some_ugly_looking_value4")
def fromString(value: String): Option[MyEnum] =
value match {
case First.value => Option(First)
case Second.value => Option(Second)
case Third.value => Option(Third)
case Fourth.value => Option(Fourth)
case _ => None
}
}
What I'm trying to achieve here is to be able to parse a string value coming from the outside into the form of the above enum. At the same time I would like to have the exhaustive pattern matching compiler warning if I don't cover all options in the match expression. What options do I have here? I don't like what I implemented above, since if this enum grows I may just forget to implement the new case clause...
Consider enumeratum like so
import enumeratum._
sealed abstract class MyEnum(override val entryName: String) extends EnumEntry
object MyEnum extends Enum[MyEnum] {
val values = findValues
case object First extends MyEnum("Some_ugly_looking_value1")
case object Second extends MyEnum("Some_ugly_looking_value2")
case object Third extends MyEnum("Some_ugly_looking_value3")
case object Fourth extends MyEnum("Some_ugly_looking_value4")
}
MyEnum.withName("Some_ugly_looking_value1") // res1: MyEnum = First
Now we do not have to fiddle with pattern match when adding a new case object.

Scala Multiple Nested Case Clases

Good day everyone, I create the following case class on SCALA:
sealed abstract class Value;
case class U(name: String) extends Value
case class L(name: String) extends Value
case class B(name: String) extends Value
sealed abstract class Term
case class Var(name: String) extends Term //variable name
case class Val(value: Value) extends Term //value
sealed abstract class Pattern //patterns
case class BGP(subject: Term, predicate: Term, obj: Term) extends Pattern
case class And( pat1: Pattern, pat2: Pattern) extends Pattern
case class Filter(pred: Predicate, pattern: Pattern ) extends Pattern
def function(p: Pattern): Unit = p match {
case BGP(Var(x), Val(y), Val(z)) => {
val con:conv = new conv()
val valor:Value = Val(y).value
}
Then, as you can see, BGP contains Term and extends to pattern, Val contains Values and extends to Term, and U,L,B contains Strings and extends to Value,
In my function I want to access to the strings that contains the U or L or B case classes, the variable valor = Val(y).value contains a U class for example, but when I write valor.XXXX don't appear me the name option. The big question is How can I do to accesss to the String name from U?
You just define it on Value which btw could be a trait.
sealed trait Value {
def name: String
}
case class U(name: String) extends Value
case class L(name: String) extends Value
case class B(name: String) extends Value

Scala: Pattern matching a generic case class for "concrete" cases

let's say I have:
sealed trait Data
final case class TextData() extends Data
final case class ImageData() extends Data
final case class MetaData[D <: Data](data: D) extends Data
I want to pattern match Data objects. I have defined:
def getDataTypeName(data: Data): String ={
data match {
case TextData()=>"text"
case ImageData()=>"image"
//MetaData(MetaData(_))=>"metaMeta1" //a
case _:MetaData[MetaData[_]]=>"metaMeta2" //b
//case MetaData(data:Data)=>"meta"+ getDataTypeName(data) //c
//MetaData(TextData(_))=>"metaMeta1" //d
case MetaData(_)=>"meta"
}
}
I want to be able to see if my data is a meta-metadata, meta-TextData, etc.
Line a give me an error, saying:
constructor cannot be instantiated to expected type; found :
inst$A$A.MetaData[D] required: A$A115.this.Data case
MetaData(MetaData(_))=>"metaMeta1"
I would prefer to have something resembling line a. alternatively something like line c would be nice too.
I also tried line b. This one generates some warnings:
non-variable type argument A$A115.this.MetaData[] in
type pattern A$A115.this.MetaData[A$A115.this.MetaData[]] is
unchecked since it is eliminated by erasure
case :MetaData[MetaData[]]=>"metaMeta"
^
I get this warning for both MetaData[_]s. My understanding is that the said pattern is reduced to _ after type erasure which is consistent with my test results.
I therefore want to know if there is a way of matching against MetaMetaData, MetaTextData ,MetaMetaMetaData etc.
Alternatively, is my design using sealed class in a way they shouldn't be used? What alternative design would you suggest?
I made a change to the MetaData() definition to make this work.
sealed trait Data
final case class TextData() extends Data
final case class ImageData() extends Data
final case class MetaData(data: Data) extends Data
def printType(data: Data): String = data match {
case TextData()=>"text"
case ImageData()=>"image"
case MetaData(d)=>"meta:" + printType(d)
}
val md = MetaData( MetaData(TextData()) )
printType(md) // res0: String = meta:meta:text
sealed trait Data
final case class TextData() extends Data
final case class ImageData() extends Data
final case class MetaData[D <: Data](data: D) extends Data
and
def getDataTypeName(data: Data): String ={
data match {
case TextData()=>"text"
case ImageData()=>"image"
case MetaData(MetaData(_))=> "metaMeta1"
case MetaData(TextData())=> "textMeta1"
case MetaData(ImageData())=> "imgMeta1"
}
}
testing on REPL:
scala> getDataTypeName(TextData())
res1: String = text
scala> getDataTypeName(MetaData(TextData()))
res2: String = textMeta1
scala> getDataTypeName(MetaData(MetaData(TextData())))
res3: String = metaMeta1

Type parameter that contains type parameter itself

For returning the status and key of a database insert, I created the following structure:
sealed trait InsertResult[+I] extends Product with Serializable
object InsertResult {
case class Created[+I](insertedId: I) extends InsertResult[I]
case object DuplicateKey extends InsertResult[Nothing]
}
The type parameter I is the type of the key that was created (e.g. UUID). If the insert succeds, a Created[I] is returned; in case of a duplicate key error, the DuplicateKey object is returned.
This works, but since the database adds more metadata than just the key, I want to replace I by Entity[I, T], where I still is the key type whereas T represents the type of inserted object (so basically an InsertResult[Entity[I, T]]). However, I'm having trouble doing that, just replacing the type parameter gives me an error for parameter insertedId (I and T cannot be resolved).
How do the type and method signatures have to look when replacing I with Entity[I, T]?
Wouldn't this be enough for your use case?
case class Entity[I, T](id: I, obj: T)
sealed trait InsertResult[I, T] extends Product with Serializable
object InsertResult {
case class Created[I, T](entity: Entity[I, T]) extends InsertResult[I, T]
case object DuplicateKey extends InsertResult[Nothing, Nothing]
}
package com.example
object Answer extends App {
case class Entity[I, T](id:I, obj:T)
sealed trait InsertResult extends Product with Serializable {}
object InsertResult {
case class Created[I, T](entity:Entity[I, T]) extends InsertResult
case object DuplicateKey extends InsertResult
}
object Storage {
var data = Map.empty[Any, Any]
def insert[I, T](entity:Entity[I, T]):InsertResult =
data.get(entity.id) match {
case Some(_) => InsertResult.DuplicateKey
case None =>
data += entity.id -> entity.obj
InsertResult.Created(entity)
}
}
val entities = Seq(Entity(1, "foo"), Entity(2, "bar"), Entity(1, "foobar"))
entities map Storage.insert foreach println
}