Is there a way to tag a generated shapeless LabelledGeneric with information about annotations on fields?
For example
case class Example(#someAnnotation foo: Foo, bar: Bar)
could we get something like
FieldType["foo" with Annotations(someAnnotation :: HNil), Foo] :: FieldType["bar", Bar] :: HNil
?
You can have HLists FieldType['foo, Foo] :: FieldType['bar, Bar] :: HNil and Some[someAnnotation] :: None.type :: HNil using type classes shapeless.LabelledGeneric, shapeless.Annotations
implicitly[LabelledGeneric.Aux[Example,
FieldType[Witness.`'foo`.T, Foo] :: FieldType[Witness.`'bar`.T, Bar] :: HNil]]
implicitly[Annotations.Aux[someAnnotation, Example,
Some[someAnnotation] :: None.type :: HNil]]
Please notice that 'foo, 'bar (written as Witness.`'foo`.T, Witness.`'bar`.T for technical reasons) are singleton types that are subtypes of Symbol, not String.
If you really need an HList of intersections (with) or tuples (fieldName, annotation) then you can zip the HLists using one of type classes shapeless.ops.hlist.Zip, shapeless.ops.hlist.ZipOne, shapeless.ops.hlist.ZipWith.
Related
lets say I have the following class hierarchy:
sealed trait Animal
case class Cat(isFriendly: Boolean) extends Animal
case class Dog(color: String) extends Animal
case class Fish(isFreshWater: Boolean) extends Animal
Now I have an instance of type
Option[Cat] :: Option[Dog] :: Option[Fish] :: HNil
But there is a restriction on the instance. It can only be of one of the following forms
Some(Cat(???)) :: None :: None :: HNil
or
None :: Some(Dog(???)) :: None :: HNil
or
None :: None :: Some(Fish(???)) :: HNil
First, excuse any incoherence - it is part of a larger problem that I am trying to solve that is not yet well articulated
Second, the ??? is just my contrived place holder for real instance such as:
None :: Some(Dog(brown)) :: None :: HNil
Thing is, I am rather new to shapeless and I don't exactly know if the value of the ??? makes a difference.
Onwards to the question
Is there a way to "iterate" over the HList and extract the Some?
I understand that when speaking generically it is not possible as shown in the following two questions. But I wonder whether adding the restrictions I set above would make a difference
https://stackoverflow.com/a/28598157/101715
https://stackoverflow.com/a/29572541/101715
As explained in the link you gave, such an operation is only possible on HList if your values are statically typed as Some and None, so that the compiler can do anything about it.
If you have additional information that what the type gives (here, the fact that exactly one of the options can be a Some), it means that you're using the wrong type, since types are the information you have on values at compile time. In this case, the type you should use is Coproduct:
type AnimalCoproduct = Cat :+: Dog :+: Fish :+: CNil
val dog = Coproduct[AnimalCoproduct](Dog("brown"))
Now, back to your question, assuming you know which are None and which are Some at compile time.
First, you need to check which HList have the property that they are a list of None.
trait IsNoneList[L <: HList]
object IsNoneList {
//all values in an HNil are None, since there aren't any
implicit val hnil: IsNoneList[HNil] = new IsNoneList[HNil] {}
//if all the values in the tail are None, and the head is None, then all the values are None
implicit def hcons[T <: HList: IsNoneList]: IsNoneList[None.type :: T] = new IsNoneList[None.type :: T] {}
}
So now, if there is an implicit IsNoneList[L] in scope, it means that L is an HList of None.type. Let's do the same with the property we're looking for:
trait IsOneSomeHList[L <: HList] {
type OneSome
def get(l: L): OneSome
}
object IsOneSomeHList {
type Aux[L <: HList, O] = IsOneSomeHList[L] { type OneSome = O }
def apply[L <: HList](implicit L: IsOneSomeHList[L]) = L
// if the tail is full of None, and the head is a Some, then the property is true
implicit def someHead[A, T <: HList: IsNoneList]: Aux[Some[A] :: T, A] = new IsOneSomeHList[Some[A] :: T] {
type OneSome = A
def get(l: Some[A] :: T) = l.head.get
}
//if the head is None, and the tail has the property, then the HCons also has the property, with the same extraction function
implicit def noneHead[T <: HList](implicit T: IsOneSomeHList[T]): Aux[None.type :: T, T.OneSome] = new IsOneSomeHList[None.type :: T] {
type OneSome = T.OneSome
override def get(l: ::[None.type, T]): T.OneSome = T.get(l.tail)
}
}
Notice that if we have an implicit IsOneSomeHList[L] in scope, we know that L has the property we want, but we can also use this implicit to get the type and the value of the only Some in the list.
EDIT
Let's give an example:
val cat = Some(Cat(isFriendly = true)) :: None :: None :: HNil
IsOneSomeHList[Some[Cat] :: None.type :: None.type :: HNil].get(cat) == Cat(true)
I am learning shapeless, and currently I am trying to create a function that does the following:
given a type of an HList it returns the HList of Nones, with the Option types corresponding to given HList type.
For instance:
create[String :: Int :: HNil] // returns None[String] :: None[Int] :: HNil
So the logic is the following:
def create[A <: HList] {
type HT = ??? //somehow getting Head type
type TT = ??? //somehow getting Tail type
// if HT is HNil HNil else Option.empty[HT] :: create[TT]
}
Looks like the HT and TT can be provided byIsHCons
def createHList[L <: HList](implicit ihc: IsHCons[L]): HList = {
type HT = ihc.H
type TT = ihc.T
//
}
But that rises two problems
How to compare types?
Compiler can not find IsHCons[TT] for recursive call. (How to get ISHCons[TT] from IsHCons[L]? It is not even possible for HNil!)
I think that I can get around the (1), by providing implicits for HNil and non HNil, so the compiler will pick up the right implicit, depending on the type.
Am I moving towards the right direction?
Given that, may be it is worth to ask more general question. Given the HList of Monoids, is it possible to derive zero HList, consisting of zeros of give monoids?
Thanks!
It's fairly easy to define Monoid instance for every HList where each element type has its Monoid instance:
trait Monoid[T] {
def zero: T
def plus(t1: T, t2: T): T
}
object Monoid {
implicit val HNilMonoid: Monoid[HNil] = new Monoid[HNil] {
def zero = HNil
def plus(hn1: HNil, hn2: HNil) = HNil
}
implicit def HConsMonoid[H, T <: HList](implicit hm: Monoid[H], tm: Monoid[T]): Monoid[H :: T] =
new Monoid[H :: T] {
def zero = hm.zero :: tm.zero
def plus(ht1: H :: T, ht2: H :: T) =
hm.plus(ht1.head, ht2.head) :: tm.plus(ht1.tail, ht2.tail)
}
}
(actually, I would expect shapeless to be able to derive the above automatically, but I'm not an expert on shapeless)
Now, assuming that we have Monoid[Int] and Monoid[String] defined elsewhere, you can just:
implicitly[Monoid[Int :: String :: HNil]].zero
which is exactly what you want, i.e. a HList of zeros.
I want to have a generic list of different types and I want to get objects with specific type.
I have this structure:
trait Parent
case class A() extends Parent
case class B() extends Parent
case class C() extends Parent
I may define different lists with different size of:
val list:List[Parent] = A() :: B() :: C() :: C() ::Nil //or any size or ordering
I want to cast this in runtime to Hlist of A,B,C,C. .toHlist[] need types of elements which should be infer in runtime.
What would be Shapeless way of it? Is there any way to convert it to tuples?
You can't do it. Note that
should be infer in runtime
is a contradiction. Type inference works in compile time only.
In other words, when you write something like
val list: A :: B :: C :: C :: HNil = ...
the type of list variable is known at compile time. It is impossible to assign a type to a variable at runtime, it just doesn't make sense. Suppose it would be possible, and you would have a magical method toHlistMagical:
val list: List[Parent] = A() :: B() :: C() :: C() :: Nil
val hlist = list.toHlistMagical // infers to A :: B :: C :: C :: HNil
Now let's change it a bit:
def getHlist(list: List[Parent]) = list.toHlistMagical
Now what return type do you expect for this function to have? Bear in mind that it can be called with various lists, not only those which contain A, B, C, C instances in this order:
getHlist(C() :: B() :: A() :: A() :: Nil)
It could be Any, but then you can just go with List anyway because there is no HList anymore, so you don't get any extra type safety.
That's the reason why toHlist needs types:
def getHlist(list: List[Parent]) = list.toHlist[A :: B :: C :: C :: HNil]
getHlist now has return type Option[A :: B :: C :: C :: HNil], because you specified the exact type you want, and toHlist will be able to perform runtime validation of the list structure and return Some if the list does actually contain these types in this order or None if it does not.
The following function can convert your list to a HList:
def listToHList(x: List[_]): HList = {
if(x == Nil) HNil
else x.head :: listToHList(x.tail)
}
For your sample, you can use it like the following:
listToHList(list).asInstanceOf[A :: B :: C :: HNil]
I'm building a generic function that takes in a HList of the form F[T1] :: ... :: F[Tn] :: HNil, converts that into a F[T1 :: ... :: Tn :: HNil] and then needs to pass that into a block that was passed in. However, in order for that to work, I need to extract the HList type in that F[_]. I've found something remotely relevant under Shapeless' hlistconstraints:
/**
* Type class witnessing that every element of `L` has `TC` as its outer type constructor.
*/
trait UnaryTCConstraint[L <: HList, TC[_]]
...but this can only be used to verify that the hlist passed in is indeed made up of just F[_]; there seems to be no way however to extract that _ bit so to say to a hlist of its own.
Where should I be looking to find something to do the job? Or should I just not expect to find anything out of the box and instead build the type computation myself?
Disclosure: this question is an auxilliary to Generic transform/fold/map over tuple/hlist containing some F[_] but is nevertheless at least as useful as a standalone question, in my opinion.
Looks like Sequencer already does this:
import scala.language.higherKinds
class Wrap[TC[_]] {
def foo[L1 <: HList, L2 <: HList](xs: L1)(implicit
seq: Sequencer.Aux[L1, TC[L2]] // L2 is the type we're looking for
): L2 = ???
}
val ret = new Wrap[Option].foo(1.some :: 2.some :: HNil)
// ret now has type Int :: Int :: HNil
...but I'm currently unable to think of a way to make this nicer by
getting rid of the wrapper class;
having Scala infer TC to be Option.
Note: I think this is somewhat useful as an answer but I'm not accepting it — hoping someone will come up with a more generic and nicer looking solution.
In order to achieve that is necessary that F has at least applicative capabilities. Once you ensure that, it's quite possible as in this code:
trait HApplicative[Eff[_], InL <: HList] extends {
type OutL <: HList
def product: InL => Eff[OutL]
}
object HApplicative {
case class HAppAux[Eff[_], InL <: HList, OutL0 <: HList](zipper: InL => Eff[OutL0]) extends HApplicative[Eff, InL] {
type OutL = OutL0
override def product: InL => Eff[OutL0] = zipper
}
implicit def nilHApp[Eff[_]](implicit app: Applicative[Eff]): HApplicative[Eff, HNil] {type OutL = HNil} =
HAppAux[Eff, HNil, HNil](_ => app.pure(HNil))
implicit def consHApp[Eff[_], InH, InT <: HList](
implicit tailHApp: HApplicative[Eff, InT],
app: Applicative[Eff]
): HApplicative[Eff, Eff[InH] :: InT] {type OutL = InH :: tailHApp.OutL} = HAppAux[Eff, Eff[InH] :: InT, InH :: tailHApp.OutL] {
case (inH: Eff[InH]) :: inT => app.map2(inH, tailHApp.product(inT)) {
case (head, tail) => head :: tail
}
}
def product[Eff[_],InL<:HList](inL:InL)(implicit happ:HApplicative[Eff,InL]):happ.OutL = happ.product(inL)
}
Say that I have a trait Show[T] such as the one in Scalaz: https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Show.scala#L9
I also have a Shapeless HList that may look like "1" :: 2 :: 3L :: HNil.
Is there a way to find the Show instance for each element and apply shows such that I end up with "1" :: "2" :: "3L" :: HNil?
If any element were of a type that did not have an implicit Show instance in scope I would want a compile error.
I think that if I build up an HList of the Show instances I should be able to use zipApply to get the HList I want, but I don't know if there is a way to get have Scala infer the HList of Show instances instead of me building it up by hand.
If your goal is to apply the Show instances and you don't otherwise care about building up an HList of them, the easiest approach is probably to use a polymorphic function:
import scalaz._, Scalaz._, shapeless._
val xs = "1" :: 2 :: 3L :: HNil
object show extends Poly1 {
implicit def forShowable[A: Show] = at[A](_.shows)
}
val strings: String :: String :: String :: HNil = xs map show
You could get an HList of the instances by changing the Poly1 a bit:
object showInstance extends Poly1 {
implicit def forShowable[A: Show] = at[A](_ => Show[A])
}
In some cases it can be useful to define your own type class to collect evidence that you've got certain type class instances:
trait AllShowable[L <: HList, S <: HList] {
def instances: S
}
implicit object hnilAllShowable extends AllShowable[HNil, HNil] {
def instances = HNil
}
implicit def hlistAllShowable[H: Show, TL <: HList, TS <: HList](
implicit ts: AllShowable[TL, TS]
) = new AllShowable[H :: TL, Show[H] :: TS] {
def instances = Show[H] :: ts.instances
}
But usually mapping with a polymorphic function that requires the instances will work just fine.