4
votes

While learning shapeless, I wonder, why this doesn't compile:

def someHList[H <: HList]: H = HNil

since the HNil object extends the HNil trait which extends HList?

What is the right way to define a method in a trait which returns some HList, which is only implemented in by the extending class?

I'd like to do something like the following:

trait Parent {
  def someHList[H <: HList]: H
}

object Child1 extends Parent {
  def someHList[H <: HList] = HNil
}

object Child2 extends Parent {
  def someHList[H <: HList] = 1 :: "two" :: HNil
}

Any advice is appreciated. Thanks!

EDIT

To elaborate as I realize what I underspecified in my original question:

1.) It is desirable to not have to specify H explicitly in each implementing class, but rather let it be inferred (at the call site?).

2.) I'd like to use HNil as a default implementation in the parent trait which can optionally be overridden in subclasses. My example probably should have been:

trait Parent {
  def someHList[H <: HList]: H = HNil
}

object Child extends Parent {
  override def someHList[H <: HList] = 1 :: "two" :: HNill
}
2
This question's title is a bad pun/reference to this classic language puzzle, which is structurally similar: en.wikipedia.org/wiki/When_a_white_horse_is_not_a_horseRyan

2 Answers

8
votes

HNil object is an HList. But it is not necessary H.

definition like

def someHList[H <: HList]: H = HNil

should be read as

for any type H, subtype of HList there is a way to construct it's member and it will be HNil

which is obviously erroneous

Thing you are trying to do, as I feel, is rephrased version of it to

there is a type H, subtype of HList and a way to construct it's member

If so you can use type member like this:

import shapeless._

trait Parent {
  type H <: HList
  def someHList: H
}

object Child1 extends Parent {
  type H = HNil
  def someHList: H = HNil
}

object Child2 extends Parent {
  type H = Int :: String :: HNil
  def someHList: H = 1 :: "two" :: HNil
}

Update

You can also refactor it a little bit to make some types infer automatically like

abstract class Parent[H <: HList](val someList: H)
object Child1 extends Parent(HNil: HNil)
object Child2 extends Parent(1 :: "two" :: HNil)

You may note that type for HNil is set manually, that is because type of object HNil is a HNil.typesubtype of HNil, which can lead the compiler wrong way sometimes

4
votes

If just use HList as a return type than everything works fine:

trait Parent {
  def someHList: HList
}

object Child1 extends Parent {
  def someHList = HNil
}

object Child2 extends Parent {
  def someHList = 1 :: "two" :: HNil
}

Or for the updated version of the question:

trait Parent {
  def someHList: HList = HNil
}

object Child2 extends Parent {
  override def someHList = 1 :: "two" :: HNil
}