1

I was trying to convert generic type to HList:

trait ToHList[T] { type Out <: HList def apply(value: T): Out } trait LowPriorityToHList { implicit def default[T]: ToHList.Aux[T, T :: HNil] = new ToHList[T] { override type Out = T :: HNil override def apply(value: T): T :: HNil = value :: HNil } } object ToHList extends LowPriorityToHList { type Aux[T, Out0] = ToHList[T] { type Out = Out0 } def apply[T](implicit toHList: ToHList[T]): Aux[T, toHList.Out] = toHList implicit def toHList[T, Repr <: HList, N <: Nat](implicit gen: Generic.Aux[T, Repr], len: Length.Aux[Repr, N], lt: LT[Nat._0, N]): ToHList.Aux[T, Repr] = new ToHList[T] { override type Out = Repr override def apply(value: T): Repr = gen.to(value) } } object Main extends App { println(ToHList.apply[Int].apply(1)) // expected 1 :: HNil println(ToHList.apply[(Int, Int)].apply((1, 2))) // expected 1 :: 2 :: HNil } 

I intended to give priority to ToHList.toHList over ToHList.default but this code cause following compilation error:

[error] ToHList.scala:39:24: ambiguous implicit values: [error] both method toHList in object ToHList of type [T, Repr <: shapeless.HList, N <: shapeless.Nat](implicit gen: shapeless.Generic.Aux[T,Repr], implicit len: shapeless.ops.hlist.Length.Aux[Repr,N], implicit lt: shapeless.ops.nat.LT[shapeless.Nat._0,N])ToHList.Aux[T,Repr] [error] and method default in trait LowPriorityToHList of type [T]=> ToHList.Aux[T,T :: shapeless.HNil] [error] match expected type ToHList[(Int, Int)] [error] println(ToHList.apply[(Int, Int)].apply((1, 2))) // expected 1 :: 2 :: HNil 

I want to give priority to ToHList.toHList over ToHList.default. How can I fix this error?

1 Answer 1

3

If both toHList and default are applicable then they have the same priority, so they make ambiguity. Indeed, although default is defined in a low-priority super-trait but it's more specific than toHList. See details in Why is this implicit ambiguity behaviour happening?

So there is no reason to put default into a low-priority super-trait, this will not make desired impact. But if you put toHList and default into the same object, default will win as more specific. And from expected 1 :: 2 :: HNil it seems you want vice versa toHList to win. You can use shapeless.LowPriority

object ToHList { type Aux[T, Out0] = ToHList[T] { type Out = Out0 } def apply[T](implicit toHList: ToHList[T]): Aux[T, toHList.Out] = toHList implicit def toHList[T, Repr <: HList, N <: Nat](implicit gen: Generic.Aux[T, Repr], len: Length.Aux[Repr, N], lt: LT[Nat._0, N] ): ToHList.Aux[T, Repr] = new ToHList[T] { override type Out = Repr override def apply(value: T): Repr = gen.to(value) } implicit def default[T](implicit lowPriority: LowPriority ): ToHList.Aux[T, T :: HNil] = new ToHList[T] { override type Out = T :: HNil override def apply(value: T): T :: HNil = value :: HNil } } 

Alternatively in this specific case you can use shapeless.Refute, shapeless.OrElse

implicit def default[T](implicit orElse: OrElse[Refute[Generic[T]], Generic.Aux[T, HNil]] ): ToHList.Aux[T, T :: HNil] = new ToHList[T] { override type Out = T :: HNil override def apply(value: T): T :: HNil = value :: HNil } 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.