Limit generic case class parameters with upper bound in scala - 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

Related

difference between case f:Int and case f # Int

what is the difference between case x:Int and case x # Int? In the following example, why doesn't case x#Int gets matched when an Int argument is passed?
scala> def matchInt (x:Any) = x match {
| case x:Int => println("got int"+x) //this matches with Int
| case _ => println("no int "+x)
| }
matchInt: (x: Any)Unit
scala> matchInt(1)
got int1
scala> matchInt("2")
no int 2
scala> def matchInt (x:Any) = x match {
| case x # Int => println("got int"+x) //this doesn't matches with Int
| case _ => println("no int "+x)
| }
matchInt: (x: Any)Unit
scala> matchInt("2")
no int 2
scala> matchInt(1)
no int 1
scala>
x:Int means "x of type Int". x#Int means "x that is a type Int".
The latter is pretty useless in this case.
We use x: Int for Type pattern matching and at times you may want to add a variable to a pattern. You can do this with the following general syntax: variableName # pattern.
For x: Int your pattern matching is fine to match the type of the x.
For variableName # pattern, look the example where we are matching a various pattern:
scala> case class Test(t1: String, t2: String)
defined class Test
scala> object Test2 extends App {
| def matchType(x: Any): String = x match {
| case y # List(1, _*) => s"$y" // works; prints the list
| case y # Some(_) => s"$y" // works, returns "Some(Hiii)"
| case y # Test("t1", "t2") => s"$y" // works, returns "Test(t1,t2)"
| }
| }
defined object Test2
scala> Test2.matchType(List(1,2,3))
res2: String = List(1, 2, 3)
scala> Test2.matchType(Some("Hiii"))
res3: String = Some(Hiii)
scala> Test2.matchType(Test("t1","t2"))
res4: String = Test(t1,t2)

Checking the arity of a method object

Given any method, eg
def add(x: Int, y: Int) = {
x + y
}
Can I use any inspection/reflection library to obtain the arity of add?
Something like arity(add), since the function object does not seems to have a property to provide that information.
One type-safe solution is to overload your Node's constructor for each Function* trait that corresponds to the arity of a function that you will pass as a parameter:
scala> class Node {
| def this(f: Function0[Int]) = { this(); println(0) }
| def this(f: Function1[Int, Int]) = { this(); println(1) }
| def this(f: Function2[Int, Int, Int]) = { this(); println(2) }
| }
defined class Node
scala> new Node(add _)
2
res7: Node = Node#427128a6
If your situation requires a reflection-based approach somehow, you can count the number of parameters of the apply method that all the Function* traits share, as follows:
scala> def arity(f: AnyRef): Option[Int] = {
| val apply = f.getClass.getMethods.find(_.getName == "apply")
| apply.map(_.getParameterCount)
| }
arity: (f: AnyRef)Option[Int]
scala> arity(add _)
res0: Option[Int] = Some(2)
Or you might want to consider using typed patterns:
def arity(f: AnyRef): Int = f match {
case _: Function0[_] => 0
case _: Function1[_, _] => 1
case _: Function2[_, _, _] => 2
...
}

Merging multiple case (in match/case) in Scala

I have the code that instance.get returns value, and based on the type I process accordingly.
instance.get match {
case v:Range => {
val sizeInBytes = util.conversion.Util.getBytesForBits(v.size)
val value = v.decode(contentByteArray.slice(index, index + sizeInBytes))
index += sizeInBytes
res(key) = value
}
case v:Encoding => {
val sizeInBytes = util.conversion.Util.getBytesForBits(v.size)
val value = v.decode(contentByteArray.slice(index, index + sizeInBytes))
index += sizeInBytes
res(key) = value
}
...
}
In the code, I have duplication for the Range and Encoding type. How can I merge the two cases?
I tried the | operator, but it doesn't work.
case v:Range | v:Encoding
This can't work, because Range.size and Encoding.size are two completely different methods despite the fact that they are named the same. And same is true for Range.decode and Edncoding.decode.
So, when you write v.size, the type of v has to be known, it has to be either v:Encoding or v:Range, not v:Encoding|v:Range.
How to fix this? Make a common trait like this:
trait SomethingWithDecodeAndSize {
def size: Int
def decode(bytes: Array[Byte]): Whatever
}
And then, change the definitions of Range and Encoding:
class Range extends SomethingWithDecodeAndSize { ... }
class Encoding extends SomethingWithDecodeAndSize { ... }
Now you can just do case v: SomethingWithDecodeAndSize => ... in your match clause.
Also ... Don't do instance.get, that's bad taste. Do instead
instance match {
Some(v: SomethingWithDecodeAndSize) => ...
}
Update
If you cannot modify the definitions of the original classes, you can use an extractor:
object SomethingWithDecodeAndSize {
def unapply(a: Any): Option[SomethingWithDecodeAndSize] = a match {
case r: Range => Some(new SomethingWithDecodeAndSize {
def size = r.size
def decode(bytes: Array[Byte]) = r.decode(bytes)
})
case r: Encoding => Some(new SomethingWithDecodeAndSize {
def size = r.size
def decode(bytes: Array[Byte]) = r.decode(bytes)
})
case _ => None
}
}
Now, you can do case Some(SomethingWithDecodeAndSize(v)) => ... in your match.
An alternate solution to #Dima's in case you can't change definition of Range and Encoding (and there is no supertype with required methods):
trait RangeOrEncoding {
def size: Int
def decode(bytes: Array[Byte]): Whatever
}
implicit def liftRange(r: Range): RangeOrEncoding = new RangeOrEncoding {
def size = r.size
def decode(bytes: Array[Byte]) = r.decode(bytes)
}
// similar conversion for Encoding
// can also be a local def
private def handleRangeOrEncoding(v: RangeOrEncoding) = {
val sizeInBytes = util.conversion.Util.getBytesForBits(v.size)
val value = v.decode(contentByteArray.slice(index, index + sizeInBytes))
index += sizeInBytes
res(key) = value
}
instance match {
case Some(v: Range) => handleRangeOrEncoding(v)
case Some(v: Encoding) => handleRangeOrEncoding(v)
...
}
I remember the cheerleaders in high school asking us, "How loose is your goose?"
scala> class C { def f(i: Int) = 2 * i }
defined class C
scala> class D { def f(i: Int) = 3 * i }
defined class D
scala> def test(x: Any) = x match { case y: { def f(i: Int): Int } => y.f(42) }
<console>:11: warning: a pattern match on a refinement type is unchecked
def test(x: Any) = x match { case y: { def f(i: Int): Int } => y.f(42) }
^
warning: there was one feature warning; re-run with -feature for details
test: (x: Any)Int
scala> test(new C)
res0: Int = 84
scala> test(new D)
res1: Int = 126
scala> test(42)
java.lang.NoSuchMethodException: java.lang.Integer.f(int)
at java.lang.Class.getMethod(Class.java:1786)
at .reflMethod$Method1(<console>:11)
at .test(<console>:11)
... 32 elided
I believe the answer was: "Loose, baby, loose."
Edit:
scala> import reflect.runtime._,universe._,language.reflectiveCalls
import reflect.runtime._
import universe._
import language.reflectiveCalls
scala> class C { def f(i: Int) = 2 * i }
defined class C
scala> class D { def f(i: Int) = 3 * i }
defined class D
scala> def f[A](a: A)(implicit tt: TypeTag[A]) = a match {
| case b: { def f(i: Int): Int }
| if tt.tpe <:< typeOf[{ def f(i: Int): Int }] =>
| b.f(42)
| }
<console>:19: warning: a pattern match on a refinement type is unchecked
case b: { def f(i: Int): Int }
^
f: [A](a: A)(implicit tt: reflect.runtime.universe.TypeTag[A])Int
scala> f(new C)
res0: Int = 84
scala> f(new D)
res1: Int = 126
scala> f(3) // now an ordinary MatchError
scala.MatchError: 3 (of class java.lang.Integer)
at .f(<console>:18)
... 32 elided
So you can express it as an ordinary type bounds:
scala> def f[A <: { def f(i: Int): Int }](a: A) = a.f(42)
f: [A <: AnyRef{def f(i: Int): Int}](a: A)Int
scala> f(new C)
res3: Int = 84
scala> f(17)
<console>:20: error: inferred type arguments [Int] do not conform to method f's type parameter bounds [A <: AnyRef{def f(i: Int): Int}]
f(17)
^
<console>:20: error: type mismatch;
found : Int(17)
required: A
f(17)
^
You still need to accept the cost of the reflective call, of course.

How to create case class from List[String]?

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.

Scala, Currying on multi parameter-group method including implicit params?

After having discovered that currying multi parameter-groups method is possible, I am trying to get a partially applied function which requires implicit parameters.
It seams not possible to do so. If not could you explain me why ?
scala> def sum(a: Int)(implicit b: Int): Int = { a+b }
sum: (a: Int)(implicit b: Int)Int
scala> sum(3)(4)
res12: Int = 7
scala> val partFunc2 = sum _
<console>:8: error: could not find implicit value for parameter b: Int
val partFunc2 = sum _
^
I use a singleton object to create this partially applied function and I want to use it in a scope where the implicit int is defined.
That is because you don't have an implicit Int in scope. See:
scala> def foo(x: Int)(implicit y: Int) = x + y
foo: (x: Int)(implicit y: Int)Int
scala> foo _
<console>:9: error: could not find implicit value for parameter y: Int
foo _
^
scala> implicit val b = 2
b: Int = 2
scala> foo _
res1: Int => Int = <function1>
The implicit gets replaced with a real value by the compiler. If you curry the method the result is a function and functions can't have implicit parameters, so the compiler has to insert the value at the time you curry the method.
edit:
For your use case, why don't you try something like:
object Foo {
def partialSum(implicit x: Int) = sum(3)(x)
}
scala> object MySingleton {
| def sum(a: Int)(implicit b: Int): Int = { a+b }
|
|
| def caller(a: Int) = {
| implicit val b = 3; // This allows you to define the partial below
| def pf = sum _ // and call sum()() without repeating the arg list.
| pf.apply(a)
| }
| }
defined module MySingleton
scala> MySingleton.caller(10)
res10: Int = 13