Scala 3 - generic programming with Product - scala

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))

Related

Implicit Conversion from tuple to custom type parameterized by size of tuple

I have a type X[D <: Int] that I would like to convert to implicitly from a tuple.
Specifically a tuple of length D whose members are all Int should be convertable to an X[D].
I am not able to write a Conversion[A, B] to properly perform this.
Scastie playground with my attempt
https://scastie.scala-lang.org/R3BEymtWSaqTCtDTsirTKw
import scala.language.implicitConversions
// tuple types
type TupleAllAre[A] = [T <: Tuple] =>> T =:= TupleElementsAre[T, A]
type TupleElementsAre[T <: Tuple, A] <: Tuple = T match
case A *: rest => A *: TupleElementsAre[rest, A]
case EmptyTuple => EmptyTuple
// tuple types work
val s1 = summon[TupleAllAre[Int][(Int, Int)]]
// Expected error
// val s2 = summon[TupleAllAre[Int][(Int, String, Int)]]
// target classes / methods
class X[D <: Int](x: Seq[Int]):
def size: Int = x.size
def definition[D <: Int](x: X[D]): Unit = ???
def def_with_conversion[T <: Tuple: TupleAllAre[Int]] (tuple: T) (using c: Conversion[T, X[Tuple.Size[T]]]): X[Tuple.Size[T]] = c(tuple)
class M[D <: Int]:
def apply(x: X[D]): Unit = ???
val m1 = M[2]()
extension [T <: Tuple: TupleAllAre[Int]] (tuple: T)
def ext(using c: Conversion[T, X[Tuple.Size[T]]]): X[Tuple.Size[T]] = c(tuple)
// conversion
given [T <: Tuple: TupleAllAre[Int]]: Conversion[T, X[Tuple.Size[T]]] = ???
// works with a non generic conversion
// given Conversion[(Int, Int), X[2]] = ???
// should all work
val x1: X[2] = (16, 16)
val d1 = definition[2](2, 2)
val d2: X[2] = def_with_conversion(2, 2)
val a1 = m1(2, 2)
val e1: X[2] = (2, 2).ext
If what you are trying to achieve is to have a type that X[D] that represent a tuple of D elements all Integer this is a way to achieve it:
import scala.compiletime.ops.int.*
type S = Singleton & Int
type X[D <: S] = D match
case 0 => EmptyTuple
case _ => Int *: X[-[D, 1]]
val x: X[2] = (1,2)
def definition[D <: S](x: X[D]): Unit = ()
val x1: X[2] = (16, 16)
println(x1)
val d1 = definition[2]((2, 2))
https://scastie.scala-lang.org/alfonsorr/pk2Avg4YToKaWtKUKxinJA

Why scala cannot resolve the T parameter

When I try to call the following function:
def sortBy[T, R](seq: Seq[T], by: T => R)(implicit ordering: Ordering[R]): Seq[T] = {
...
}
with this:
case class MyClass(f1: Int, f2: Int)
val o1 = MyClass(1, 2)
val o2 = MyClass(3, 4)
sortBy(Seq(o1, o2), x => x.f1)
I get compilation error "cannot resolve symbol f1"
However when I call it with explicit types it works:
sortBy[MyClass,Int](...)
My question is why scala cannot infer these types automatically?
As one of the ways to solve
def sortBy[T, R](seq: Seq[T], by: T => R)(implicit ordering: Ordering[R]): Seq[T] = ???
case class MyClass(f1: Int, f2: Int)
val o1 = MyClass(1, 2)
val o2 = MyClass(3, 4)
def orderFunc(a: MyClass): Int = a.f1
sortBy(Seq(o1, o2), orderFunc)

Lifting a function which takes implicit parameter using functor (Scalaz7)

Just started learning Scalaz. Here is my code
trait Monoid[A] {
def mappend(a1: A, a2: A): A
def mzero: A
}
object Monoid {
implicit val IntMonoid: Monoid[Int] = new Monoid[Int] {
def mappend(a1: Int, a2: Int): Int = a1 + a2
def mzero: Int = 0
}
implicit val StringMonoid: Monoid[String] = new Monoid[String] {
def mappend(a1: String, a2: String): String = a1 + a2
def mzero: String = ""
}
}
trait MonoidOp[A] {
val F: Monoid[A]
val value: A
def |+|(a2: A): A = F.mappend(value, a2)
}
object MonoidOp{
implicit def toMonoidOp[A: Monoid](a: A): MonoidOp[A] = new MonoidOp[A]{
val F = implicitly[Monoid[A]]
val value = a
}
}
I have defined a function (just for the sake of it)
def addXY[A: Monoid](x: A, y: A): A = x |+| y
I want to lift it so that it could be used using Containers like Option, List, etc. But when I do this
def addXYOptioned = Functor[Option].lift(addXY)
It says error: could not find implicit value for evidence parameter of type scalaz.Monoid[A]
def addOptioned = Functor[Option].lift(addXY)
How to lift such functions?
Your method addXY needs a Monoid[A] but there is no Monoid[A] in scope when used in addXYOptioned, so you also need to add the Monoid constraint to addXYOptioned.
The next problem is that Functor.lift only lifts a function A => B, but we can use Apply.lift2 to lift a function (A, B) => C.
Using the Monoid from Scalaz itself :
import scalaz._, Scalaz._
def addXY[A: Monoid](x: A, y: A): A = x |+| y
def addXYOptioned[A: Monoid] = Apply[Option].lift2(addXY[A] _)
We could generalize addXYOptioned to make it possible to lift addXY into any type constructor with an Apply instance :
def addXYApply[F[_]: Apply, A: Monoid] = Apply[F].lift2(addXY[A] _)
addXYApply[List, Int].apply(List(1,2), List(3,4))
// List[Int] = List(4, 5, 5, 6)
addXYApply[Option, Int].apply(1.some, 2.some)
// Option[Int] = Some(3)

Scala upper type bound

class P(name: String)
class E(_name: String, role: String) extends P(_name)
def testF[T <: P](x: List[T]): List[T] = x
val le = List(new E("Henry", "Boss"))
class Test[R <: E](l: List[R]) {
def r[O <: P] (): List[O] = testF(l)
}
I get:
Error:(8, 38) type mismatch;
found : List[R]
required: List[O]
def r[O <: P] (): List[O] = testF(l)
My intuition suggests that this should have worked because T has a tighter upper type bound than O.
**** EDIT ****
def findNN[A, B <: A, C <: A, T] (seq: Seq[B], n: Int, center: C, distance: (A, A) => T)
(implicit ord: Ordering[T]): Seq[B] = {
import ord._
val ds = seq map ( (a: A) => distance(a, center))
val uds = ds.distinct
//#TODO: replace quickSelect with median-of algorithm if necessary
val kel = quickSelect(uds, n)
val z = seq zip ds
val (left, _) = z partition Function.tupled((_, d: T) => d <= kel)
left map {t => t._1}
}
OK, let's have a look at the example above.
The superclass A provides the method distance.
I would like to use the function findNN on a seq[B] having a center in a class C. distance should work because it works on A
Based on feedback provided, there's no way to simplify the type signatures above.
You made misprinting, you need >: rather then :<
class P(name: String)
class E(_name: String, role: String) extends P(_name)
def testF[T >: P](): List[T] = List(new P("Henry"))
You are trying to limit the type of the result using a type parameter R (with an upper bound type E), while you are not using the type R in your function.
An example of a correct use of a type parameter (with an upper bound):
def testF[T <: P](list: List[T]): List[T] = list
testF(List(new P("Tom")))
// List[P] = List(P#43bc21f0)
testF(List(new E("Jerry", "Mouse")))
// List[E] = List(E#341c1e65)
An incorrect use of a type parameter:
// does not compile, what is A ??
def foo[A]: List[A] = List("bar")

How to implement an heterogeneous container in Scala

I need an heterogeneous, typesafe container to store unrelated type A, B, C.
Here is a kind of type-level specification :
trait Container {
putA(a: A)
putB(b: B)
putC(c: C)
put(o: Any) = { o match {
case a: A => putA(a)
case b: B => putB(b)
case c: C => putC(c)
}
getAllAs : Seq[A]
getAllBs : Seq[B]
getAllCs : Seq[C]
}
Which type is best suites to backed this container ?
Is it worth creating a Containerable[T] typeclass for types A, B, C ?
thks.
As other have suggested, you can leverage shapeless' Coproduct type. Here's an example.
// let's define a Coproduct of the two types you want to support
type IS = Int :+: String :+: CNil
// now let's have a few instances
val i = Coproduct[IS](42)
val i2 = Coproduct[IS](43)
val s = Coproduct[IS]("foo")
val s2 = Coproduct[IS]("bar")
// let's put them in a container
val cont = List(i, s, i2, s2)
// now, do you want all the ints?
val ints = cont.map(_.select[Int]).flatten
// or all the strings?
val strings = cont.map(_.select[String]).flatten
// and of course you can add elements (it's a List)
val cont2 = Coproduct[IS](12) :: cont
val cont3 = Coproduct[IS]("baz") :: cont2
Now this is of course not the most intuitive API for a generic container, but can easily encapsulate the logic inside a custom class using a Coproduct for representing the multiple types.
Here's a sketch of an implementation
import shapeless._; import ops.coproduct._
class Container[T <: Coproduct] private (underlying: List[T]) {
def ::[A](a: A)(implicit ev: Inject[T, A]) =
new Container(Coproduct[T](a) :: underlying)
def get[A](implicit ev: Selector[T, A]) =
underlying.map(_.select[A]).flatten
override def toString = underlying.toString
}
object Container {
def empty[T <: Coproduct] = new Container(List[T]())
}
Example
scala> type IS = Int :+: String :+: CNil
defined type alias IS
scala> val cont = 42 :: "foo" :: "bar" :: 43 :: Container.empty[IS]
cont: Container[IS] = List(42, foo, bar, 43)
scala> cont.get[Int]
res0: List[Int] = List(42, 43)
scala> cont.get[String]
res1: List[String] = List(foo, bar)
Miles Sabin wrote a post on unboxed union types; this is implemented as a CoProduct in his shapeless library:
shapeless has a Coproduct type, a generalization of Scala's Either to an arbitrary number of choices
I am definitely not an expert on shapeless, but if you create a new question with or edit your question with the shapeless tag then you can get any assistance needed with using CoProduct
You should look at Shapeless's HList or Coproduct; I wouldn't reinvent this myself.
Here is a first version, but I would to abstract over type :
trait Container {
def putInt(i: Int)
def putString(s: String)
def put(o: Any) = o match {
case i: Int => putInt(i)
case s: String => putString(s)
}
def getInts() : Seq[Int]
def getStrings() : Seq[String]
}
class MutableContainer extends Container {
val ints = mutable.ArrayBuffer[Int]()
val strings = mutable.ArrayBuffer[String]()
override def putInt(i: Int): Unit = ints += i
override def putString(s: String): Unit = strings += s
override def getStrings(): Seq[String] = strings
override def getInts(): Seq[Int] = ints
}
object TestContainer extends App {
val mc = new MutableContainer()
mc.put("a")
mc.put("b")
mc.put(1)
println(mc.getInts())
println(mc.getStrings())
}
Now trying to abstract over type
trait Container {
def getInts() : Seq[Int]
def getStrings() : Seq[String]
def put[T](t: T)
//def get[T] : Seq[T]
}
class MutableContainer extends Container {
val entities = new mutable.HashMap[Class[_], mutable.Set[Any]]() with mutable.MultiMap[Class[_], Any]
override def getStrings(): Seq[String] = entities.get(classOf[String]).map(_.toSeq).getOrElse(Seq.empty).asInstanceOf[Seq[String]] //strings
override def getInts(): Seq[Int] = entities.get(classOf[Int]).map(_.toSeq).getOrElse(Seq.empty).asInstanceOf[Seq[Int]]
//override def get[T]: Seq[T] = entities.get(classOf[T]).map(_.toSeq).getOrElse(Seq.empty).asInstanceOf[Seq[T]]
override def put[T](t: T): Unit = entities.addBinding(t.getClass, t)
}
trait Containable[T] {
def typ : String
}
trait Cont {
implicit object IntContainable extends Containable[Int] {
override def typ: String = "Int"
}
implicit object StringContainable extends Containable[String] {
override def typ: String = "String"
}
}
object TestContainer extends App {
val mc = new MutableContainer()
mc.put("a")
mc.put("b")
mc.put(1)
println(mc.getInts())
println(mc.getStrings())
println(mc.entities.keys)
}
But i've got a problem with java.lang.Integer and Int…