HList/KList from class values - scala

I want to be able to create a class/trait that behaves somewhat like an enumeration (HEnum in the first snippet below). I can't use a plain enumeration because each enum value could have a different type (though the container class will be the same): Key[A]. I'd like to be able to construct the enum roughly like this:
class Key[A](val name: String)
object A extends HEnum {
val a = new Key[String]("a")
val b = new Key[Int]("b")
val c = new Key[Float]("c")
}
And then I'd like to be able to perform more or less basic HList operations like:
A.find[String] // returns the first element with type Key[String]
A.find("b") // returns the first element with name "b", type should (hopefully) be Key[Int]
So far I've been playing with an HList as the underlying data structure, but constructing one with the proper type has proven difficult. My most successful attempt looks like this:
class Key[A](val name: String)
object Key {
def apply[A, L <: HList](name: String, l: L): (Key[A], Key[A] :: L) = {
val key = new Key[A](name)
(key, key :: l)
}
}
object A {
val (a, akeys) = Key[String, HNil]("a", HNil)
val (b, bkeys) = Key[Int, Key[String] :: HList]("b", akeys)
val (c, ckeys) = Key[Float, Key[Int] :: HList]("c", bkeys)
val values = ckeys // use this for lookups, etc
def find[A]: Key[A] = values.select[A]
def find[A](name: String): Key[A] = ...
}
The problem here is that the interface is clunky. Adding a new value anywhere besides the end of the list of values is error prone and no matter what, you have to manually update values any time a new value is introduced. My solution without HList involved a List[Key[_]] and error prone/unsafe casting to the proper type when needed.
EDIT
I should also mention that the enum example found here is not particularly helpful to me (although, if that can be adapted, then great). The added compiler checks for exhaustive pattern matches are nice (and I would ultimately want that) but this enum still only allows a homogeneous collection of enum values.

Related

How to pass array of types in Scala?

I am trying to create a method that will take a list of Objects and a list of Types and then check whether a particular object matches a particular type.
I tried to create a dummy example. In the following code validate method takes list of objects and a List of type then tried to validate the types of each object. I know following code is wrong but I just created to communicate the problem, it would be really great if you can tell me how can I do this in Scala.
object T extends App {
trait MyT
case class A(a: Int) extends MyT
case class B(b: Int) extends MyT
def validate(values: Seq[Any], types: Seq[MyT]): Seq[Boolean] =
for ((v, ty: MyT) ← values.zip(types)) yield v.isInstanceOf[ty]
// create objects
val a = A(1)
val b = B(1)
// call validate method
validate(Seq(a, b), Seq(A, B))
}
Types are not values, so you can't have a list of them.
But, you may use Class instead:
def validate(values: Seq[Any], types: Seq[Class[_ <: MyT]]): Seq[Boolean] =
values.lazyZip(types).map {
case (v, c) =>
c.isInstance(v)
}
Which can be used like this:
validate(Seq(a, b), Seq(classOf[A], classOf[B]))
// res: Seq[Boolean] = List(true, true)
You can see the code running here.
However, note that this will break if your classes start to have type parameters due to type erasure.
Note, I personally don't recommend this code since, IMHO, the very definition of validate is a code smell and I would bet you have a design error that lead to this situation.
I recommend searching for a way to avoid this situation and solve the root problem.

Scala cast to generic type

I'm confused about the generic type. I expect that 2.asInstanceOf[A] is cast to the type A, meanwhile, it's cast to Int.
Besides that, the input is java.lang.Long whereas the output is a list of Int (according to the definition the input and the output should be the same type). Why is that?
def whatever[A](x: A): List[A] = {
val two = 2.asInstanceOf[A]
val l = List(1.asInstanceOf[A],2.asInstanceOf[A])
println(f"Input type inside the function for 15L: ${x.getClass}")
println(f"The class of two: ${two.getClass}, the value of two: $two")
println(f"The class of the first element of l: ${l.head.getClass}, first element value: ${l.head}")
l
}
println(f"Returned from whatever function: ${whatever(15L)}")
the outupt:
Input type inside the function for 15L: class java.lang.Long
The class of two: class java.lang.Integer, the value of two: 2
The class of the first element of l: class java.lang.Integer, first element value: 1
Returned from whatever function: List(1, 2)
a.asInstanceOf[B] means:
Dear compiler;
Please forget what you think the type of a is. I know better. I know that if a isn't actually type B then my program could blow up, but I'm really very smart and that's not going to happen.
Sincerely yours, Super Programmer
In other words val b:B = a.asInstanceOf[B] won't create a new variable of type B, it will create a new variable that will be treated as if it were type B. If the actual underlying type of a is compatible with type B then everything is fine. If a's real type is incompatible with B then things blow up.
Type erasure. For the purposes of type checking 2 is cast to A; but at a later compilation stage A is erased to Object, so your code becomes equivalent to
def whatever(x: Object): List[Object] = {
val two = 2.asInstanceOf[Object]
val l = List(1.asInstanceOf[Object],2.asInstanceOf[Object])
println(f"Input type inside the function for 15L: ${x.getClass}")
println(f"The class of two: ${two.getClass}, the value of two: $two")
println(f"The class of the first element of l: ${l.head.getClass}, first element value: ${l.head}")
l
}
2.asInstanceOf[Object] is a boxing operation returning a java.lang.Integer.
If you try to actually use the return value as a List[Long] you'll eventually get a ClassCastException, e.g.
val list = whatever(15L)
val x = list(0)
x will be inferred to be Long and a cast inserted to unbox the expected java.lang.Long.
The answer from #jwvh is on point. Here I'll only add a solution in case you want to fix the problem of safely converting an Int to an A in whatever, without knowing what A is. This is of course only possible if you provide a way to build a particular A from an Int. We can do this in using a type-class:
trait BuildableFromInt[+A] {
def fromInt(i: Int): A
}
Now you only have to implicitly provide BuildableFromInt for any type A you wish to use in whatever:
object BuildableFromInt {
implicit val longFromInt: BuildableFromInt[Long] = Long.box(_)
}
and now define whatever to only accept compliant types A:
def whatever[A : BuildableFromInt](x: A): List[A] = {
val two = implicitly[BuildableFromInt[A]].fromInt(2)
// Use two like any other "A"
// ...
}
Now whatever can be used with any type for which a BuildableFromInt is available.

Get sequence of types from HList in macro

Context: I'm trying to write a macro that is statically aware of an non-fixed number of types. I'm trying to pass these types as a single type parameter using an HList. It would be called as m[ConcreteType1 :: ConcreteType2 :: ... :: HNil](). The macro then builds a match statement which requires some implicits to be found at compile time, a bit like how a json serialiser might demand implicit encoders. I've got a working implementation of the macro when used on a fixed number of type parameters, as follows:
def m[T1, T2](): Int = macro mImpl[T1, T2]
def mImpl[T1: c.WeakTypeTag, T2: c.WeakTypeTag](c: Context)(): c.Expr[Int] = {
import c.universe._
val t = Seq(
weakTypeOf[T1],
weakTypeOf[T2]
).map(c => cq"a: $c => externalGenericCallRequiringImplicitsAndReturningInt(a)")
val cases = q"input match { case ..$t }"
c.Expr[Int](cases)
}
Question: If I have a WeakTypeTag[T] for some T <: HList, is there any way to turn that into a Seq[Type]?
def hlistToSeq[T <: HList](hlistType: WeakTypeTag[T]): Seq[Type] = ???
My instinct is to write a recursive match which turns each T <: HList into either H :: T or HNil, but I don't think that kind of matching exists in scala.
I'd like to hear of any other way to get a list of arbitrary size of types into a macro, bearing in mind that I would need a Seq[Type], not Expr[Seq[Type]], as I need to map over them in macro code.
A way of writing a similar 'macro' in Dotty would be interesting too - I'm hoping it'll be simpler there, but haven't fully investigated yet.
Edit (clarification): The reason I'm using a macro is that I want a user of the library I'm writing to provide a collection of types (perhaps in the form of an HList), which the library can iterate over and expect implicits relating to. I say library, but it will be compiled together with the uses, in order for the macros to run; in any case it should be reusable with different collections of types. It's a bit confusing, but I think I've worked this bit out - I just need to be able to build macros that can operate on lists of types.
Currently you seem not to need macros. It seems type classes or shapeless.Poly can be enough.
def externalGenericCallRequiringImplicitsAndReturningInt[C](a: C)(implicit
mtc: MyTypeclass[C]): Int = mtc.anInt
trait MyTypeclass[C] {
def anInt: Int
}
object MyTypeclass {
implicit val mtc1: MyTypeclass[ConcreteType1] = new MyTypeclass[ConcreteType1] {
override val anInt: Int = 1
}
implicit val mtc2: MyTypeclass[ConcreteType2] = new MyTypeclass[ConcreteType2] {
override val anInt: Int = 2
}
//...
}
val a1: ConcreteType1 = null
val a2: ConcreteType2 = null
externalGenericCallRequiringImplicitsAndReturningInt(a1) //1
externalGenericCallRequiringImplicitsAndReturningInt(a2) //2

Implementing Path dependent Map types in Shapeless

I am wanting to put together a map which contains path dependent type mapping from outer to inner:
import shapeless._
import shapeless.ops.hlist._
abstract class Outer {
type Inner
def inner: Inner
}
private case class Derp(hl: HList) {
def get(outer: Outer): outer.Inner = hl.select[outer.Inner]
def put(outer: Outer)(inner: outer.Inner): Derp = Derp(hl :: inner :: HNil)
def delete(outer: Outer)(c: outer.Inner): Derp = ???
}
The theory is that by using an HList, I can avoid using type selectors and ensure that programs can only get at an instance of Inner that was created by Outer.
Is this possible, or even a good idea? Most of the HList questions seem to be about arity and case classes, and I feel like I'm working outside the box.
Note that I am aware of https://stackoverflow.com/a/30754210/5266 but this question is about the Shapeless HList implementation in particular -- I don't know how to remove elements from HList and potentially return Option[outer.Inner].
First, you'll almost certainly need to parameterize Derp:
case class Derp[L <: HList](hl: L)
This is because whenever you have a Derp, the compiler will need to know what its static HList type is in order to do anything useful.
HList types encode the information about every type in the list – like
type Foo = Int :: String :: Boolean :: HNil
As soon as you say hl: HList, that information is lost.
Next, you'll want to properly specify the return types of your operations:
def get[O <: Outer](o: O)(implicit selector: Selector.Aux[L, o.type, o.Inner]): o.Inner = selector(hl)
def put[O <: Outer](o: O)(i: o.Inner): Derp[FieldType[o.type, o.Inner] :: L] = copy(hl = field[o.type](i))
This is "tagging" each Inner value with the Outer's type, so you can retrieve it later (which is what the Selector.Aux does). All interesting stuff in Shapeless happens through typeclasses that come with it (or that you define yourself), and they rely on type information to work. So the more type information you can retain in your operations, the easier it will be.
In this case, you'll never return Option, because if you try to access a value that isn't in the map, it won't compile. This is typically what you'd use HList for, and I'm not sure it matches your use case.
Shapeless also has HMap, which uses key-to-value mappings like a normal Map. The difference is that each key type can map to a different value type. This seems more in line with your use case, but it's organized a bit differently. To use HMap, you define a relation, as a type function. A type function is a typeclass with a dependent type:
trait MyRelation[Key] {
type Value
}
object MyRelation {
type Aux[K, V] = MyRelation[K] { type Value = V }
implicit val stringToInt: Aux[String, Int] = new MyRelation[String] { type Value = Int }
implicit val intToBool: Aux[Int, Boolean] = new MyRelation[Int] { type Value = Boolean }
}
Now you can define an HMap over MyRelation, so when you use String keys you'll add/retrieve Int values, and when you use Int keys you'll add/retrieve Boolean values:
val myMap = HMap[MyRelation.Aux]("Ten" -> 10, 50 -> true)
val myMap2 = myMap + ("Fifty" -> 50)
myMap2.get("Ten") // Some(10), statically known as Option[Int]
myMap2.get(44) // None, statically known as Option[Boolean]
This is a bit different from your example, in that you have a value with a dependent type, and you want to use the outer type as the key, and the inner type as the value. It's possible to express this as a relation for HMap as well, by using K#Inner =:= V as the relation. But it will often surprise you by not working, because path-dependent types are tricky and really depend on concrete subtypes of the outer (which will require a lot of boilerplate) or singleton types (which will be difficult to pass around without losing the necessary type information).

Scala covariant doc example allows apples and oranges

See this implementation follows the upper bound example http://docs.scala-lang.org/tutorials/tour/upper-type-bounds.html
class Fruit(name: String)
class Apple (name: String) extends Fruit(name)
class Orange(name: String) extends Fruit(name)
class BigOrange(name:String) extends Orange(name)
class BigFLOrange(name:String) extends BigOrange(name)
// Straight from the doc
trait Node[+B ] {
def prepend[U >: B ](elem: U)
}
case class ListNode[+B](h: B, t: Node[B]) extends Node[B] {
def prepend[U >:B ](elem: U) = ListNode[U](elem, this)
def head: B = h
def tail = t
}
case class Nil[+B ]() extends Node[B] {
def prepend[U >: B ](elem: U) = ListNode[U](elem, this)
}
But this definition seems to allow multiple unrelated things in the same container
val f = new Fruit("fruit")
val a = new Apple("apple")
val o = new Orange("orange")
val bo = new BigOrange("big orange")
val foo :ListNode[BigOrange] = ListNode[BigOrange](bo, Nil())
foo.prepend(a) // add an apple to BigOrangeList
foo.prepend(o) // add an orange to BigOrangeList
val foo2 : ListNode[Orange] = foo // and still get to assign to OrangeList
So I am not sure this is a great example in the docs. And, question, how doe I modify the constraints so that..this behaves more like a List?
User #gábor-bakos points out that I am confusing invariance with covariance. So I tried the mutable list buffer. It does not later allow apple to be inserted into an Orange list Buffer, but it is not covariant
val ll : ListBuffer[BigOrange]= ListBuffer(bo)
ll += bo //good
ll += a // not allowed
So..can my example above (ListNode) be modified so that
1. it is covariant (it already is)
2 It is mutable, but mutable like the ListBuffer example (will not later allow apples to be inserted into BigOrange list
A mutable list cannot/should not be covariant in its argument type.
Exactly because of the reason you noted.
Suppose, you could have a MutableList[Orange], that was a subclass of a MutableList[Fruit]. Now, there is nothing that would prevent you from making a function:
def putApple(fruits: MutableList[Fruit], idx: Int) =
fruits(idx) = new Apple
You can add Apple to the list of Fruits, because Apple is a Fruit, nothing wrong with this.
But once you have a function like that, there is no reason you can't call it like this:
val oranges = new MutableList[Orange](new Orange, new Orange)
putApple(oranges, 0)
This will compile, since MutableList[Orange] is a subclass of MutableList[Fruit]. But now:
val firstOrange: Orange = oranges(0)
will crash, because the first element of oranges is actually an Apple.
For this reason, mutable collections have to be invariant in the element type (to answer the question you asked in the comments, to make the list invariant remove the + before B, and also get rid of the type parameter in prepend. It should just be def pretend(elem: B)).
How to get around it? The best solution is to simply not use mutable collections. You should not need them in 99% or real life scala code. If you think you need one, there is a 99% you are doing something wrong.
The major thing that you are probably missing is that prepend does not modify a list. In the line val foo2 : ListNode[Orange] = foo the list foo is still of type ListNode[BigOrange] and given covariance of parameters this assignment is valid (and there is nothing particularly awkward about it). Compiler will prevent you from assigning Fruits to Oranges (in this case assigning an Apple to an Orange is hazardous) but you have to save modified lists beforehand:
val foo: ListNode[Fruit] = ListNode[BigOrange](bo, Nil()).prepend(a).prepend(o)
val foo2: ListNode[Orange] = foo // this is not valid
Moreover your Node definition lacks return type for prepend - thus compiler infers wrong return type (Unit instead of ListNode[U]).
Here's fixed version:
trait Node[+B] {
def prepend[U >: B ](elem: U): Node[U]
}