Shapeless: Trying to restrict HList elements by their type
Asked Answered
P

1

7

Question 1 - Basic LUBConstraints

My first try playing around with existing LUBConstraints fails for missing evidence (see code block below). Any hint why? Isn't an empty list a valid list of longs? no element violates the constraint.

import shapeless.ops.coproduct
import shapeless.{::, :+:, Coproduct, HNil, HList}

object testLUBConstraints {
  import shapeless.LUBConstraint._

  // !!! see comment on question - this satisfies the implicit below!!! 
  // implicit val hnilLUBForLong = new LUBConstraint[HNil.type, Long] {}

  def acceptLong[L <: HList : <<:[Long]#λ](l: L) = true
  val validLong = acceptLong(1l :: HNil)

  val validEmpty = acceptLong(HNil)
  //  => WHY??? Error: could not find implicit value for evidence parameter of type shapeless.LUBConstraint[shapeless.HNil.type,Long]
  //  MY EXPECTATION WAS: 'implicit def hnilLUB[T] = new LUBConstraint[HNil, T] {}' defined within LUBConstraint companion should provide so

  // val invalid = acceptLong(1.0d :: HNil) -> fails due to missing evidence (as expected)
}

Any help appreciated.

Question 2 - Own Constraint using Coproduct (split into a seperate question: Shapeless: own HList constraint using Coproduct)

Question 3 - restrict case classes by parameter types (split into a separat question: Shapeless: restricting case class types)

Protectionism answered 25/9, 2015 at 9:13 Comment(6)
I managed to get around the first ERROR (in code block of Question 1) by locally declaring implicit val hnilLUBForLong = new LUBConstraint[HNil.type, Long] {} - It seems that the implicit value provided by the declared method doesn't exactly type match: LUBConstraint[HNil, Long] =!= LUBConstraint[HNil.type, Long] Intention or even a bug?Protectionism
working on the implicit resolution of CPConstraint (see Question 2) Having val hlLong: ::[Long, HNil] = 1L :: HNil and implicitly looking up a CPConstraint for it with implicit val cpcLong = implicitly[CPConstraint[hlLong.type, CPType]] I tried implicit val scpcEmptyLong1: CPConstraint[::[Long,HNil], CPType] = new CPConstraint[::[Long,HNil], CPType] {}, which does NOT fit the implicit searched but implicit val scpcEmptyLong2: CPConstraint[hlLong.type, CPType] = new CPConstraint[hlLong.type, CPType] {} would. - WHY??? What's the difference?Protectionism
This should probably be three separate questions. In the first case you can fix the issue by explicitly typing HNil as HNil: acceptLong(HNil: HNil) should compile.Steffi
ok - I will try to split the questions.Protectionism
@TravisBrown - thanks, explicitly typing helps.Protectionism
@TravisBrown Wouldn't a slightly more open declaration avoid an explicit typing: something like implicit def hnilLUB[HN <: HNil, T] = new LUBConstraint[HN, T] {} - would this be worth a proposal for improvement?Protectionism
S
6

The inferred type of the value HNil will be the singleton type HNil.type, which is a proper subtype of HNil. Because type classes like LUBConstraint are invariant, any available instance for HNil won't be found if you're asking for an instance for HNil.type.

There's been some discussion of changing the definition of HNil so that this would work, but it's not trivial, and it's not clear that all the implications of the change are desirable. In the meantime you can write HNil: HNil to upcast the value.

Steffi answered 25/9, 2015 at 22:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.