I'm trying to make this rather silly example work, planning to then extend it to something more meaningful.
But no luck so far: I get could not find implicit value for parameter ihc
What am I missing?
sealed trait Field[T] { def name: String }
case class IntegerField(name: String) extends Field[Int]
val year = IntegerField("year")
val test = (year :: 23 :: HNil) :: (year :: 2 :: HNil) :: HNil
type TypedMap = IntegerField :: Int :: HNil
def get[L <: HList : <<:[TypedMap]#λ]
(key: IntegerField, list: L)
(implicit ihc: IsHCons.Aux[L, TypedMap, L]
): Option[Int] = {
if( list == HNil ) return None
val elem: TypedMap = list.head
if( elem.head == key ) Some(elem.tail.head)
else get(key, list.tail)
}
get(year, test)
When you write IsHCons.Aux[L, TypedMap, L] you're asking for evidence that the hlist L has head TypedMap and tail L, which would mean that it's an infinite hlist, which isn't possible, since Scala doesn't allow this kind of arbitrarily recursive type (try writing something like type Foo = Int :: Foo, for example—you'll get a "illegal cyclic reference" error). It's also probably not what you want.
In general you're unlikely to use IsHCons much in Shapeless, since it's almost always better just to indicate the structure you want in the type. For example, the following two definitions do the same thing:
import shapeless._, ops.hlist.IsHCons
def foo[L <: HList](l: L)(implicit ev: IsHCons[L]) = ev.head(l)
And:
def foo[H, T <: HList](l: H :: T) = l.head
But the second is obviously preferable (it's clearer, it doesn't require an extra type class instance to be found at compile time, etc.), and it's almost always possible to write whatever you're trying to do that way.
Also note that requiring an IsHCons instance means that the recursion here won't work as stated—you can't call get on an HNil, since the compiler can't prove that it's an HCons (because it's not).
Are you sure you need an hlist at all? If you're requiring that all members of the hlist are of type TypedMap, you might as well use Shapeless's Sized (if you want the type to capture the length) or even just a plain old List.
If you really, really want to use an HList here, I'd suggest writing a new type class:
trait FindField[L <: HList] {
def find(key: IntegerField, l: L): Option[Int]
}
object FindField {
implicit val findFieldHNil: FindField[HNil] = new FindField[HNil] {
def find(key: IntegerField, l: HNil) = None
}
implicit def findFieldHCons[H <: TypedMap, T <: HList](implicit
fft: FindField[T]
): FindField[H :: T] = new FindField[H :: T] {
def find(key: IntegerField, l: H :: T) = if (l.head.head == key)
Some(l.head.tail.head)
else fft.find(key, l.tail)
}
}
def get[L <: HList](key: IntegerField, l: L)(implicit
ffl: FindField[L]
): Option[Int] = ffl.find(key, l)
And then:
scala> get(IntegerField("year"), test)
res3: Option[Int] = Some(23)
scala> get(IntegerField("foo"), test)
res4: Option[Int] = None
This is a pretty common pattern in Shapeless—you inductively describe how to perform an operation on an empty hlist, then on an hlist with a head prepended to a tail that you know how to perform the operation on, etc.
Related
I am new to shapeless (and still low level in the learning curve of scala...) and i have some hard time with shapeless
import shapeless._
case class FooBar[T](foo: String, bar: T)
val hl = 0 :: FooBar("A", "one") :: FooBar("B", 1) :: "0" :: FooBar("C", "two") :: HNil
val l = hl.filter[FooBar[String]].toList
println(l) //List(FooBar(A,one), FooBar(C,two))
It works fine
Next step, i want to put that in function, something like
def filter[T](hl: HList): List[FooBar[T]] = ???
so i can simplify calling to
filter[String](hl)
filter[Int](hl)
naively i tested
def filter[T](hl: HList): List[FooBar[T]] = {
hl.filter[FooBar[T]].toList
}
which give
could not find implicit value for parameter partition: shapeless.ops.hlist.Partition[shapeless.HList,FooBar[T]]
after some tries playing with implicit, i still have not found the correct way to do that
Do you have any idea ?
Thank you !
If you lack some implicits then in your method you should suppose they are provided. Saying that an argument of the method is of type just HList (and not some specific L <: HList) is too rough.
Since probably you would like to specify T and not specify L (expecting that L will be inferred) try a type class + extension method
import shapeless._
import shapeless.ops.hlist.{Partition, ToTraversable}
case class FooBar[T](foo: String, bar: T)
val hl = 0 :: FooBar("A", "one") :: FooBar("B", 1) :: "0" :: FooBar("C", "two") :: HNil
trait FilterFooBar[L <: HList, T] {
def apply(l: L): List[FooBar[T]]
}
object FilterFooBar {
implicit def mkFilterFooBar[L <: HList, T, Prefix <: HList, Suffix <: HList](implicit
partition: Partition.Aux[L, FooBar[T], Prefix, Suffix],
toTraversable: ToTraversable.Aux[Prefix, List, FooBar[T]]
): FilterFooBar[L, T] = _.filter.toList
}
implicit class FilterFooBarOp[L <: HList](l: L) {
def filterFooBar[T](implicit filterFooBarInstance: FilterFooBar[L, T]): List[FooBar[T]] =
filterFooBarInstance(l)
}
println(hl.filterFooBar[String]) // List(FooBar(A,one), FooBar(C,two))
println(hl.filterFooBar[Int]) // List(FooBar(B,1))
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 have the following problem, I want to map items of an HList to another HList but Strings in the source HList should only be converted to URL if the "target" type is URL.
val name = "Stackoverflow"
val url = "https://stackoverflow.com/q"
val list = name :: url :: HNil
val mapped: String :: URL :: HNil = list.map(???)
As far as my research took me is that all the Poly stuff only cares about the input type but not about the output type. So are there ways to archive my goal ?
I don't think you're going to get exactly what you want, since Scala's implicit resolution happens before type inference (but who knows—people do things that surprise me in Scala all the time).
(Side note: the CanBuildFrom / breakOut pattern supports something similar to what you're asking for, but I don't see a way to make it work in this situation, since the source type does constrain what instances are available.)
There is a pretty standard workaround for this kind of situation, though, involving the use of a helper class to approximate partial application of type parameters. Suppose you've got a fairly straightforward type class that captures your conversion logic:
import java.net.URL
import shapeless._
trait Convert[I <: HList, O <: HList] { def apply(i: I): O }
object Convert extends LowPriorityConvertInstances {
implicit val convertHNil: Convert[HNil, HNil] = new Convert[HNil, HNil] {
def apply(i: HNil): HNil = i
}
implicit def convertHConsURL[T <: HList, TO <: HList](implicit
c: Convert[T, TO]
): Convert[String :: T, URL :: TO] = new Convert[String :: T, URL :: TO] {
def apply(i: String :: T): URL :: TO = new URL(i.head) :: c(i.tail)
}
}
sealed class LowPriorityConvertInstances {
implicit def convertHCons[H, T <: HList, TO <: HList](implicit
c: Convert[T, TO]
): Convert[H :: T, H :: TO] = new Convert[H :: T, H :: TO] {
def apply(i: H :: T): H :: TO = i.head :: c(i.tail)
}
}
Now you might try something like this:
def convert[I <: HList, O <: HList](i: I)(implicit c: Convert[I, O]): O = c(i)
But there are two problems here. The first is that if you let the type parameters be inferred, you'll always get a conversion that turns every string into a URL. You can override this behavior by explicitly providing both type parameters, but ugh.
We can (kind of) improve this situation with a helper class:
class PartiallyAppliedConvert[O <: HList] {
def apply[I <: HList](i: I)(implicit c: Convert[I, O]): O = c(i)
}
def convert[O <: HList]: PartiallyAppliedConvert[O] =
new PartiallyAppliedConvert[O]
Now you can write the following:
scala> val mapped = convert[String :: URL :: HNil](list)
mapped: shapeless.::[String,shapeless.::[java.net.URL,shapeless.HNil]] = Stackoverflow :: https://stackoverflow.com/q :: HNil
It's not exactly what you asked for, but it's pretty close, in that the only type we have to specify explicitly is the desired target type.
I would like to do something like the following:
val foo = List[B <% JValue] = 42 :: "hello" : Nil
for the compiler to know that the members of my list can be converted to JValues.
However, this does not compile. I cannot settle for a List[Any] because I have to use its members where values that can be converted into JValues are expected, say in:
def fun[A <% JValue](x: List[A]) = ...
Is there any way to solve this?
You can write
val foo: List[JValue] = List(42, "hello")
If there is no implicit conversion from these types to JValue then it won't type check.
Unfortunately you can't write it as 42 :: "hello" :: Nil because the compiler isn't smart enough to know that you want to apply the conversions on each term (you could add a type annotation on each term, but that's messy). Each :: method would have to somehow look ahead all the way to the end of the expression to check if some later method made it fit the type parameter.
However, if you want to add your own funky operator, you can constrain the types allowed by :::
implicit class pimp(xs: List[JValue]) {
def |: (x: JValue) = x :: xs
}
after which you can write stuff like this:
val foo = 42 |: "hello" |: Nil
// type is List[JValue]
(I tried parameterizing this so that it would infer the most specific common type, as :: does, but with an upper bound the compiler didn't want to play ball - see here. Maybe someone with more Scala-fu can fix it, if it's possible.)
A little improvement of |: method from Luigi Plinge's answer:
You could write
val foo: List[JValue] = 42 :: "hello" :: HNil
with an appropriate implicit conversion (using shapeless):
import shapeless._
trait HListTConv[H <: HList, T] {
def apply(l: List[T], hl: H): List[T]
}
object HListTConv {
implicit def apply0[T] = new HListTConv[HNil, T] {
def apply(l: List[T], hl: HNil): List[T] = l
}
implicit def applyN[Head, Tail <: HList, T](implicit c: HListTConv[Tail, T], conv: Head => T) =
new HListTConv[Head :: Tail, T] {
def apply(l: List[T], hl: Head :: Tail): List[T] = (hl.head: T) :: c(l, hl.tail)
}
}
implicit def hListToJValueList[H <: HList](hl: H)(implicit c: HListTConv[H, JValue]): List[JValue] = c(Nil, hl)
Test:
case class Test(s: String)
implicit def intToTest(i: Int): Test = Test(i.toString)
implicit def strToTest(s: String): Test = Test(s)
implicit def hListToListTest[H <: HList](hl: H)(implicit c: HListTConv[H, Test]): List[Test] = c(Nil, hl)
scala> val foo: List[Test] = 42 :: "hello" :: HNil
foo: List[Test] = List(Test(42), Test(hello))
You can enable the conversions with a simple set of implicits.
class JValue
implicit intToJValue(x: Int) = new JValue
implicit stringToJValue(x: String) = new JValue
val xs: List[JValue] = List(1, "hello")
For your second question you can enable wholesale list conversion with:
implicit def listToJList[A <% JValue](xs: List[A]): List[JValue] = xs
def foo[A <% JValue](x: List[A]): List[JValue] = x
This above example only works if you have a uniform type, otherwise you will need to employ a more sophisticated means, a list of heterogeneous types will unify to List[Any] in most cases.
You could come up more elegant/complicated solutions using shapeless, most employing shapless.Poly and HList's.