2017-02-03 6 views
2

Я попытался написать класс типов, SumEq5, такой, что первые два поля своего HList параметра типа добавить до 5:Проверка времени компиляции на сумму Nat's?

trait SumEq5[A] 
object SumEq5 { 
    def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev 

    implicit def sumEq5Ev[L <: HList, A <: Nat, B <: Nat](
    implicit hcons: IsHCons.Aux[L, A, B :: HNil], 
      ev: Sum.Aux[A, B, _5] 
): SumEq5[L] = new SumEq5[L] {} 
} 

Но не похоже на работу:

import shapeless._ 
import shapeless.nat._ 
import net.SumEq5 

scala> SumEq5[_0 :: _5 :: HNil] 
<console>:19: error: could not find implicit value for 
    parameter ev: net.SumEq5[shapeless.::[shapeless.nat._0,shapeless.:: 
     [shapeless.nat._5,shapeless.HNil]]] 
     SumEq5[_0 :: _5 :: HNil] 

Пожалуйста, дайте я имею в виду, почему у _0 :: _5 :: HNil нет доказательств того, что его два Nat являются равными 5.

EDIT

Обновленный вопрос на помощь Дениса Рошки в shapeless's gitter.

+0

Черта пуста, так почему бы не просто ' новый SumEq5 [L] '? – cchantep

+0

Это нормально, но это не сработает для моего ввода, нет? –

ответ

3

Дейл Wijnand и Марк Генри указывают в правильное направление, если вы хотите обобщить HList с произвольной длины, однако, если вы действительно хотите только разместить два элемента HList с, то следующее довольно простое решение,

scala> import shapeless._, nat._, ops.nat._ 
import shapeless._ 
import nat._ 
import ops.nat._ 

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

trait SumEq5[A] 

object SumEq5 { 
    def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev 

    implicit def sumEq5AB[A <: Nat, B <: Nat] 
    (implicit ev: Sum.Aux[A, B, _5]): SumEq5[A :: B :: HNil] = 
     new SumEq5[A :: B :: HNil] {} 
} 

// Exiting paste mode, now interpreting. 

defined trait SumEq5 
defined object SumEq5 

scala> SumEq5[_0 :: _5 :: HNil] 
res0: SumEq5[_0 :: _5 :: HNil]] = [email protected] 

Основное отличие в том, что экземпляр явно оштрафован за два списка элементов, а не определяется для списков вообще, при условии, что существует доказательство того, что список имеет ровно два элемента.

После обновления Дэйла, мы можем обобщить это, чтобы приспособить HList с с по меньшей мере двух (а не ровно двух) элементов, опять же без каких-либо дополнительных свидетелей,

scala> import shapeless._, nat._, ops.nat._ 
import shapeless._ 
import nat._ 
import ops.nat._ 

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

trait SumEq5[A] 

object SumEq5 { 
    def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev 

    implicit def sumEq5AB[A <: Nat, B <: Nat, T <: HList] 
    (implicit ev: Sum.Aux[A, B, _5]): SumEq5[A :: B :: T] = 
     new SumEq5[A :: B :: T] {} 
} 

// Exiting paste mode, now interpreting. 

defined trait SumEq5 
defined object SumEq5 

scala> SumEq5[_0 :: _5 :: HNil] 
res0: SumEq5[_0 :: _5 :: HNil]] = [email protected] 
+0

Действительно, намного проще. Я бы предположил, однако, что идея была дескриптором любого HList из 2 или более элементов, из которых сумма первых двух равна 5. –

+0

Обновлено для захвата этого случая. –

+0

О да, дух. Кевин, вот твой ответ. –

3

У меня есть частичный ответ для вас, то есть решение (обходное решение), не понимая, почему именно оригинал не работает должным образом.

Кажется, вы не можете спросить непосредственно для IsHCons.Aux[L, A, B :: HNil], что вам нужно сделать это по частям:

  1. IsHCons.Aux[L, A, L2], а затем
  2. IsHCons.Aux[L2, B, HNil]

Таким образом, это составляет:

import shapeless._, nat._, ops.hlist._, ops.nat._ 

trait SumEq5[A] 
object SumEq5 { 
    def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev 

    implicit def sumEq5Ev[L <: HList, L2 <: HList, A <: Nat, B <: Nat](
    implicit hcons0: IsHCons.Aux[L, A, L2], 
      hcons: IsHCons.Aux[L2, B, HNil], 
      ev: Sum.Aux[A, B, _5] 
): SumEq5[L] = new SumEq5[L] {} 
} 

object T { 
    def main(args: Array[String]): Unit = { 
    SumEq5[_0 :: _5 :: HNil] 
    } 
} 

Исходя из Miles Sabin's answer, это может быть изменен, чтобы поддерживать любой HList из 2-х или более элементов, из которых сумма двух первых 5, например, так:

import shapeless._, nat._, ops.hlist._, ops.nat._ 

trait SumEq5[A] 
object SumEq5 { 
    def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev 

    implicit def sumEq5Ev[L1 <: HList, L2 <: HList, L3 <: HList, A <: Nat, B <: Nat](
    implicit hcons1: IsHCons.Aux[L1, A, L2], 
      hcons2: IsHCons.Aux[L2, B, L3], 
      ev: Sum.Aux[A, B, _5] 
): SumEq5[L1] = new SumEq5[L1] {} 
} 

object T { 
    def main(args: Array[String]): Unit = { 
    SumEq5[_0 :: _5 :: HNil] 
    } 
} 
+2

Я вижу это много, когда люди сначала учатся работать с рекурсивными или индуктивными типами. Компилятор должен идти шаг за шагом. Кевин пытается получить экземпляр для A :: B :: HNil без предварительного экземпляра для B :: HNil без предварительного экземпляра для HNil. Решение здесь оборачивается необходимостью индуктивного процесса, добавляя еще один слой IsHCons. Чтобы сделать это индуктивно, вам нужно доказать, что B :: HNil меньше или равно _5, тогда A :: B :: HNil равен _5 в двух неявных def. –

+0

Спасибо, Дейл и Маркус! Этот ответ и комментарий определенно помогли мне узнать больше. –

Смежные вопросы