Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is an empty HList not an HList?

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
}
like image 213
Ryan Avatar asked Dec 15 '22 02:12

Ryan


2 Answers

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

like image 155
Odomontois Avatar answered Dec 21 '22 10:12

Odomontois


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
}
like image 40
dkolmakov Avatar answered Dec 21 '22 12:12

dkolmakov