How to create case class from List[String]? - scala

Here is my code
scala> s
res6: String = 2005-05-06 14:58:56 192 45.14.5.238 200 TCP_NC_MISS 1123 496 GET http c4.maxserving.com /gen.js ?site=5835&area=side_ros&group=sidebar&PageID=33364329499 - DIRECT c4.maxserving.com application/x-javascript "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)" PROXIED Web%20Advertisements - 192.16.170.44 SG-HTTP-Service - none -
scala> s.split("\\s")
res7: Array[String] = Array(2005-05-06, 14:58:56, 192, 45.14.5.238, 200, TCP_NC_MISS, 1123, 496, GET, http, c4.maxserving.com, /gen.js, ?site=5835&area=side_ros&group=sidebar&PageID=33364329499, -, DIRECT, c4.maxserving.com, application/x-javascript, "Mozilla/4.0, (compatible;, MSIE, 6.0;, Windows, NT, 5.1;, SV1;, .NET, CLR, 1.1.4322)", PROXIED, Web%20Advertisements, -, 192.16.170.44, SG-HTTP-Service, -, none, -)
scala> case class BlueCoatEvent(date: String,
| time: String,
| timeTaken: String,
| cIp: String,
| scStatus: String,
| sAction: String,
| scBytes: String,
| csBytes: String,
| csMethod: String,
| csUriScheme: String,
| csHost: String,
| csUriPath: String,
| csUriQuery: String,
| csUsername: String,
| sHierarchy: String,
| sSupplierName: String,
| rsContentType: String,
| csUserAgent: String,
| scFilterResult: String,
| scFilterCategory: String,
| xVirusId: String,
| sIp: String,
| sSiteName: String,
| xVirusDetails: String,
| xIcapErrorCode: String,
| xIcapErrorDetails: String)
defined class BlueCoatEvent
scala>
How do I create blueCoatEvent from s.split("\\s")?

See answer in Instantiating a case class from a list of parameters
Introduce meaningsful sub-types/case classes and compose BlueCoatEvent with them.

The ugly horrible way is this:
scala> case class A(x: String, y: String, z: String)
defined class A
scala> def toTuple[A <: Object](as:List[A]):Product = {
| val tupleClass = Class.forName("scala.Tuple" + as.size)
| tupleClass.getConstructors.apply(0).newInstance(as:_*).asInstanceOf[Product]
| }
toTuple: [A <: Object](as: List[A])Product
scala> val l = List("a", "b", "c")
l: List[String] = List(a, b, c)
scala> val t3 = toTuple(l).asInstanceOf[Tuple3[String, String, String]]
t3: (String, String, String) = (a,b,c)
scala> val f = A.tupled
f: ((String, String, String)) => A = <function1>
scala> f(t3)
res0: A = A(a,b,c)
You can use any way you want to convert from collection to TupleN:
Convert a Scala list to a tuple?
Is there way to create tuple from list(without codegeneration)?
I picked toTuple from there.
This is unsafe, ugly and does not save you much. You can resort to code generation, reflection or macros to get something more helpful.
Another option:
Based on this idea Applying an argument list to curried function using foldLeft in Scala we can use A.curried with HList to produce a cleaner solution:
object CaseClassFromList extends App {
sealed trait HList
final case class HCons[H, T <: HList](head : H, tail : T) extends HList {
def ::[H1](h : H1) = HCons(h, this)
override def toString = head+" :: "+tail.toString
}
trait HNil extends HList {
def ::[H1](h : H1) = HCons(h, this)
override def toString = "HNil"
}
case object HNil extends HNil
type ::[H, T <: HList] = HCons[H, T]
trait FoldCurry[L <: HList, F, Out] {
def apply(l : L, f : F) : Out
}
// Base case for HLists of length one
implicit def foldCurry1[H, Out] = new FoldCurry[H :: HNil, H => Out, Out] {
def apply(l : H :: HNil, f : H => Out) = f(l.head)
}
// Case for HLists of length n+1
implicit def foldCurry2[H, T <: HList, FT, Out]
(implicit fct : FoldCurry[T, FT, Out]) = new FoldCurry[H :: T, H => FT, Out] {
def apply(l : H :: T, f : H => FT) = fct(l.tail, f(l.head))
}
// Public interface ... implemented in terms of type class and instances above
def foldCurry[L <: HList, F, Out](l : L, f : F)
(implicit fc : FoldCurry[L, F, Out]) : Out = fc(l, f)
case class A(x: String, y: String, z: String)
//val l = List("a", "b", "c")
val lh = "a" :: "b" :: "c" :: HNil
val newA = foldCurry(lh, A.curried)
println(newA)
}
The problem again is that you can't get away from spelling out the types whether those are Tuple or HList unless you go unsafe way or some sort of code generation.

A different approach by defining a Map[String,String] over the values of an event; let
case class EventMap( data: Map[String,String])
and
def fields(cc: Product) = cc.getClass.getDeclaredFields.map(_.getName)
Then from
EventMap( fields(BlueCoatEvent) zip s.split("\\s") toMap )
we can fetch values properties for a given stringisized BlueCoatEvent.

Related

Limit generic case class parameters with upper bound in scala

I would like to limit parameter function to have parameter only case class with limited field types. Let's say i would allow only Int and String
def apply[T <: ???](t: T) {
...
}
case class OneParam(int: Int)
case class TwoParams(int: Int, str: String)
case class CompilationErrorClass(list: Array[String])
val oneParam = OneParam(1)
val twoParams = TwoParams(1, "2")
val compilationErrorClass = CompilationErrorClass(List())
val result1 = apply[OneParam](oneParam)
val result2 = apply[TwoParams](twoParams)
val result3 = apply[CompilationErrorClass](compilationErrorClass) // -- this will not compile as has not allowed upper-bound parameter type
How this trick can be done in scala?
Here we go (scala3):
import scala.deriving.Mirror.ProductOf
type AllowTypes = Int *: EmptyTuple | String *: EmptyTuple | (Int, String) | (String, Int)
def size[P <: Product](t: P)
(using p: ProductOf[P],
ev: p.MirroredElemTypes <:< AllowTypes): Int =
Tuple.fromProductTyped(t).size
scala> size(OneParam(0))
val res0: Int = 1
scala> size(TwoParams(1, ""))
val res1: Int = 2
scala> size(CompilationErrorClass(Array()))
-- Error: --------------------------------------------------------------------------------------------------------------------------------------------------
1 |size(CompilationErrorClass(Array()))
| ^
| Cannot prove that p.MirroredElemTypes <:< AllowTypes.
1 error found
Or even general solution, allow all case classes with arbitrarily int or string arguments:
scala> type Allowed[T <: Tuple] = T match
| case EmptyTuple => DummyImplicit
| case (Int | String) *: t => Allowed[t]
|
scala> import scala.deriving.Mirror.ProductOf
|
| def size[P <: Product](t: P)
| (using p: ProductOf[P],
| ev: Allowed[p.MirroredElemTypes]): Int =
| Tuple.fromProductTyped(t).size
|
def size[P <: Product](t: P)(using p: deriving.Mirror.ProductOf[P], ev: Allowed[p.MirroredElemTypes]): Int
scala> case class A(i: Int, j: Int, x: String, y: String)
// defined case class A
scala> case class X(x1: Int, x2: String, x3: Int, x4: String, x5: String)
// defined case class X
scala> case class Y(l: Long)
// defined case class Y
scala> size(A(0, 1, "", ""))
val res0: Int = 4
scala> size(X(0, "", 1, "", ""))
val res1: Int = 5
scala> size(Y(0))
-- Error: --------------------------------------------------------------------------------------------------------------------------------------------------
1 |size(Y(0))
| ^
| Match type reduction failed since selector Long *: EmptyTuple.type
| matches none of the cases
|
| case EmptyTuple => DummyImplicit
| case (Int | String) *: t => Allowed[t]
1 error found

Scala 3 - generic programming with Product

According to https://www.scala-lang.org/2021/02/26/tuples-bring-generic-programming-to-scala-3.html
I can write correctly:
def tupleToCsv[A <: Tuple : RowEncoder](tuple: A): List[String] = summon[RowEncoder[A]].encodeRow(tuple)
case class Employee(name: String, number: Int, manager: Boolean)
val t = Tuple.fromProductTyped(Employee("Bob", 42, false))
println(tupleToCsv(t)) // List(Bob, 42, false)
but I'd like write a unique method toCsv
def toCsv[A <: Product](t: A)(using m: scala.deriving.Mirror.ProductOf[A]): List[String] = {
val tuple = Tuple.fromProductTyped(t)
val aa = summon[RowEncoder[A]] // ***
aa.encodeRow(tuple)
}
and call it with toCsv(Employee("Bob", 42, false))
the compiler at row *** says:
no implicit argument of type RowEncoder[A] was found for parameter x of method summon in object Predef
where: A is a type in method toCsv with bounds <: Product
val aa = summon[RowEncoder[A]]
how can I provide automatic derivation for A ?
Here it is:
def toCsv[A <: Product](t: A)(using m: scala.deriving.Mirror.ProductOf[A], e: RowEncoder[m.MirroredElemTypes]): List[String] =
e.encodeRow(Tuple.fromProductTyped(t))

Folding over HList?

Given:
import shapeless._
case class F(x: Option[Int], y: Option[Int])
I'd like help to write a function, f:
def f(Option[Int] :: Option[Int] :: HNil): String
such that each Option[Int] is replace with the Some number or empty; and "" for HNil.
Example:
val res7 = Generic[F].to( F( Some(42), None) )
//res7: shapeless.::[Option[Int],shapeless.::
[Option[Int],shapeless.HNil]] = Some(42) :: None :: HNil
f(res7) === "42empty"
How can f be written?
You need a Poly:
object OptFolder extends Poly2{
def conv(x: Option[Int]) = x.map(_.toString).getOrElse("empty")
implicit val ff = at{ (y: String, z: Option[Int]) => y + conv(z) }
}
val lala: String = myHlist.foldLeft("")(OptFolder) //:String not required
So a Generic to transform to the HList and then a foldLeft with a well defined Poly.

Converting a case class hierarchy to scalaz.Tree with Shapeless

Consider the following hierarchy:
sealed trait Ex
case class Lit(value: Int) extends Ex
case class Var(name: Char) extends Ex
case class Add(a: Ex, b: Ex) extends Ex
I'd like to convert expressions such as val ex = Add(Lit(0),Add(Lit(1),Var('a'))) into a scalaz.Tree[Either[Class[Any],Any]]], which in this case would give:
-\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(0)
|
`- -\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(1)
|
`- -\/(class Var)
|
`- \/-(a)
Which is the most appropriate Shapeless functionality/example to start from for doing this?
First for a disclaimer: if you're planning to use this in real code, I think it's almost certainly a bad idea. It'd be better to try to use Shapeless to do what you're trying to do directly rather than going through this type-unsafe representation. But it's a fun problem, so here goes.
(Oh, and another disclaimer: this implementation is off the top of head and there may be nicer ways to accomplish this.)
First for a helper type class (note that all of the code in the three sections below will need to be defined together—you can use :paste if you're in a REPL):
import scalaz.{ Show, Tree, \/ }, scalaz.syntax.either._
import shapeless._, ops.hlist.ToTraversable
trait TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any]
}
trait LowPriorityTreeifyCc {
implicit def singleMemberTreeifyCc[A, C, R <: HList, X](implicit
gen: Generic.Aux[C, R],
ev: R <:< (X :: HNil)
): TreeifyCc[A, C] = new TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any] = Tree.Node(
c.getClass.left,
Stream(Tree.Leaf(ev(gen.to(c)).head.right))
)
}
}
object TreeifyCc extends LowPriorityTreeifyCc {
implicit def recursiveTreeifyCc[A, C, R <: HList](implicit
gen: Generic.Aux[C, R],
ts: ToTraversable.Aux[R, Stream, A]
): TreeifyCc[A, C] = new TreeifyCc[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any] =
Tree.Node(c.getClass.left, ts(gen.to(c)).map(tf(_)))
}
}
And another helper type class:
trait TreeifyAdt[A, C] {
def apply(tf: Treeify[A], c: C): Tree[Class[_] \/ Any]
}
object TreeifyAdt {
implicit def cnilTreeifyAdt[A]: TreeifyAdt[A, CNil] =
new TreeifyAdt[A, CNil] {
def apply(tf: Treeify[A], c: CNil): Tree[Class[_] \/ Any] =
sys.error("impossible")
}
implicit def cconsAdt[A, H, T <: Coproduct](implicit
cc: TreeifyCc[A, H],
ta: TreeifyAdt[A, T]
): TreeifyAdt[A, H :+: T] = new TreeifyAdt[A, H :+: T] {
def apply(tf: Treeify[A], c: H :+: T): Tree[Class[_] \/ Any] = c match {
case Inl(h) => cc(tf, h)
case Inr(t) => ta(tf, t)
}
}
}
And the type class we actually care about:
trait Treeify[A] {
def apply(a: A): Tree[Class[_] \/ Any]
}
object Treeify {
implicit def treeifyAdt[A, R <: Coproduct](implicit
gen: Generic.Aux[A, R],
adt: TreeifyAdt[A, R]
): Treeify[A] = new Treeify[A] {
def apply(a: A): Tree[Class[_] \/ Any] = adt(this, gen.to(a))
}
def toTree[A](a: A)(implicit tf: Treeify[A]): Tree[Class[_] \/ Any] = tf(a)
}
And we can use it like this:
scala> val ex: Ex = Add(Lit(0), Add(Lit(1), Var('a')))
ex: Ex = Add(Lit(0),Add(Lit(1),Var(a)))
scala> Treeify.toTree(ex).drawTree(scalaz.Show.showFromToString)
res0: String =
"-\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(0)
|
`- -\/(class Add)
|
+- -\/(class Lit)
| |
| `- \/-(1)
|
`- -\/(class Var)
|
`- \/-(a)
"
This will work for any ADT where all of the leaves either have a single member or one or more recursive members.

Passing a Shapeless Extensible Record to a Function (continued)

Considering this question : Passing a Shapeless Extensible Record to a Function, Travis's answer shows that every function taking an extensible record as parameter must have an implicit selector as parameter. I wonder if one could factorize those declarations in case we have many functions of this kind.
E.g. :
val w1 = Witness("foo1")
val w2 = Witness("foo2")
val w3 = Witness("foo3")
//Here some "magical" declarations avoiding to declara selectors in fun1, fun2, fun3 below
def fun1[L <: HList](xs: L) = ... //Access to foo1, foo2, foo3
def fun2[L <: HList](xs: L) = ... //Access to foo1, foo2, foo3
def fun3[L <: HList](xs: L) = ... //Access to foo1, foo2, foo3
Thanks
Benoit
edit on Dec 10
When trying the code of the answer, on comes with two problems :
Nothing is told about the real type of the data associated to foo1, foo2, foo3 : consequently, a function like fun1 can't use any method associated to these types. e.g., even if foo3 is a Double, it can't take its squareroot.
If I call fun1 with ("foo1"->> "hello") :: ("foo2" -> 1)::("foo3" ->> 1.2)::HNiL, the result is (hello, 1, 1.2) with type (selectors.s1.Out, selectors.s2.Out, selectors.s3.Out)
If I try to add 1 to the last value (1.2), Scala complains that it can't add an Int and a selectors.s3.Out ;but if I write :
val x = fun1(("foo1"->> "hello") :: ("foo2" -> 1)::("foo3" ->> 1.2)::HNil)
I can write :
x._3 == 1.2
and scala answers True!
I have tried to modify the code in this way, hopping that the types would be propagated, but it doesn't solve the problem. I can't even call fun1 with (foo1->> "hello") :: (foo2 -> 1)::(foo3 ->> 1.2)::HNil as parameter :
object foo1 extends FieldOf[String]
object foo2 extends FieldOf[Int]
object foo3 extends FieldOf[Double]
val w1 = Witness(foo1)
val w2 = Witness(foo2)
val w3 = Witness(foo3)
case class HasMyFields[L <: HList](implicit
s1: Selector[L, w1.T],
s2: Selector[L, w2.T],
s3: Selector[L, w3.T]
)
object HasMyFields {
implicit def make[L <: HList](implicit
s1: Selector[L, w1.T],
s2: Selector[L, w2.T],
s3: Selector[L, w3.T]
) = HasMyFields[L]
}
def fun1[L <: HList](xs: L)(implicit selectors: HasMyFields[L]) = {
import selectors._
(xs(foo1), xs(foo2), xs(foo3))
}
Is there a way to progress?
Benoit
You can define your own type class to gather the evidence that the record has the fields you need:
import shapeless._, ops.record.Selector, record._, syntax.singleton._
val w1 = Witness("foo1")
val w2 = Witness("foo2")
val w3 = Witness("foo3")
case class HasMyFields[L <: HList](implicit
s1: Selector[L, w1.T, String],
s2: Selector[L, w2.T, Int],
s3: Selector[L, w3.T, Double]
)
object HasMyFields {
implicit def make[L <: HList](implicit
s1: Selector[L, w1.T, String],
s2: Selector[L, w2.T, Int],
s3: Selector[L, w3.T, Double]
) = HasMyFields[L]
}
And then, for example:
def fun1[L <: HList](xs: L)(implicit selectors: HasMyFields[L]) = {
import selectors._
(xs("foo1"), xs("foo2"), xs("foo3"))
}
It's still a little verbose, especially since the import is necessary, but much less so than requiring all of the selectors individually as implicit parameters.
The out type of a given field can be specified using:
Selector[L, w1.T] { type Out = String }
Also, we can slightly simplify the syntax using a type constructor:
import shapeless._, ops.record.Selector, record._, syntax.singleton._
val w1 = Witness("foo1")
val w2 = Witness("foo2")
val w3 = Witness("foo3")
type HasFoo1[L <: HList] = Selector[L, w1.T] { type Out = String }
type HasFoo2[L <: HList] = Selector[L, w2.T]
type HasFoo3[L <: HList] = Selector[L, w3.T]
#implicitNotFound("${L} should have foo1, foo2 and foo3")
case class HasMyFields[L <: HList](implicit s1: HasFoo1[L], s2: HasFoo2[L], s3: HasFoo3[L])
object HasMyFields {
implicit def make[L <: HList : HasFoo1 : HasFoo2 : HasFoo3] = HasMyFields[L]
}
def fun1[L <: HList : HasMyFields](xs: L) = {
val selectors = implicitly[HasMyFields[L]]
import selectors._
(xs("foo1").length, xs("foo2"), xs("foo3"))
}
fun1(("foo1"->> "hello") :: ("foo2" ->> 1)::("foo3" ->> 1.2)::HNil)
// Does not compile: the value in foo1 is not a String
fun1(("foo1"->> 2) :: ("foo2" ->> 1)::("foo3" ->> 1.2)::HNil)