2017-02-08 16 views
2

Учитывая следующую попытку извлечения рекурсивной суммы двух HList, как показано ниже:Сумма двух идентичных HList во время компиляции?

(. Помилования Product в его имени сейчас, пожалуйста)

package net 

import shapeless._ 
import shapeless.nat._ 
import shapeless.ops.nat.{Sum, Prod, Mod} 

trait SumZippedProduct[L, M] { 
    type S 
} 
object SumZippedProduct { 

    type Aux[L, M, O] = SumZippedProduct[L, M] { 
    type S = O 
    } 

    def apply[L <: HList, M <: HList](implicit ev: SumZippedProduct[L, M]) = ev 

    // LH - L's head 
    // L - HList 
    // MH - M's head 
    // M - HList 
    // RS - Recursive Sum (L + H) 
    // CS - Current Sum (LH + RH) 
    // E - RS + CS 
    implicit def sumZippedInductiveEq5[LH <: Nat, L <: HList, MH <: Nat, M <: HList, RS <: Nat, CS <: Nat, E <: Nat](
    implicit ev: SumZippedProduct.Aux[L, M, RS], 
      curr: Sum.Aux[LH, MH, CS], 
      total: Sum.Aux[CS, RS, E] 
): SumZippedProduct[LH :: L, MH :: M] = new SumZippedProduct[LH :: L, MH :: M] { 
    type S = E 
    } 

    implicit val hnils: SumZippedProduct[HNil, HNil] = new SumZippedProduct[HNil, HNil] { 
    type S = _0 
    } 

} 

При попытке его, он работает, Я думаю, для HNil случая, но не для 1-го элемента HList:

scala> import net.SumZippedProduct 
import net.SumZippedProduct 

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

// expecting 0 (0 + 0) 
scala> SumZippedProduct[HNil, HNil] 
res0: net.SumZippedProduct[shapeless.HNil,shapeless.HNil] = [email protected] 

// expecting _4 (1 + 3) 
scala> SumZippedProduct[_1 :: HNil, _3 :: HNil] 
<console>:19: error: could not find implicit value for parameter ev: net.SumZippedProduct[shapeless.::[shapeless.nat._1,shapeless.HNil],shapeless.::[shapeless.nat._3,shapeless.HNil]] 
     SumZippedProduct[_1 :: HNil, _3 :: HNil] 
        ^

Почему не компилировать при прохождении _1 :: HNil и _3 :: HNil?

Также, как я могу получить _0 в res0?

scala> res0.S 
<console>:20: error: value S is not a member of net.SumZippedProduct[shapeless.HNil,shapeless.HNil] 
     res0.S 
      ^

Примечание. Я ценю, если такая реализация уже существует в бесформенной, но я задаю этот вопрос, чтобы узнать.

ответ

2

Вы должны использовать Aux в обратном типе своих имплицитов. В противном случае бетонный тип S потеряется. Для метода вызова apply вы также должны использовать более точный тип возврата по той же причине. С SumZippedProduct распространяется AnyRef вы можете просто использовать ev.type; он не будет более точным, чем это.

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

import shapeless._ 
import shapeless.nat._ 
import shapeless.ops.nat.{Sum, Prod, Mod, ToInt} 

trait SumZippedProduct[L, M] { 
    type S <: Nat 
    final def S(implicit toInt: ToInt[S]): Int = toInt() 
} 
object SumZippedProduct { 

    type Aux[L, M, O] = SumZippedProduct[L, M] { 
    type S = O 
    } 

    def apply[L <: HList, M <: HList](implicit ev: SumZippedProduct[L, M]): ev.type = ev 

    implicit def sumZippedInductiveEq5[LH <: Nat, L <: HList, MH <: Nat, M <: HList, RS <: Nat, CS <: Nat, E <: Nat](
    implicit ev: SumZippedProduct.Aux[L, M, RS], 
      curr: Sum.Aux[LH, MH, CS], 
      total: Sum.Aux[CS, RS, E] 
): SumZippedProduct.Aux[LH :: L, MH :: M, E] = new SumZippedProduct[LH :: L, MH :: M] { 
    type S = E 
    } 

    implicit val hnils: SumZippedProduct.Aux[HNil, HNil, _0] = new SumZippedProduct[HNil, HNil] { 
    type S = _0 
    } 

} 

// Exiting paste mode, now interpreting. 

import shapeless._ 
import shapeless.nat._ 
import shapeless.ops.nat.{Sum, Prod, Mod} 
defined trait SumZippedProduct 
defined object SumZippedProduct 

scala> SumZippedProduct[HNil, HNil] 
res1: SumZippedProduct.<refinement>.type = [email protected] 

scala> val a: res1.S = _0 
a: res1.S = [email protected] 

scala> SumZippedProduct[_1 :: HNil, _3 :: HNil] 
res2: SumZippedProduct.Aux[shapeless.::[shapeless.nat._1,shapeless.HNil],shapeless.::[shapeless.nat._3,shapeless.HNil],this.Out] = [email protected] 

scala> val a: res2.S = _4 
a: res2.S = Succ() 

scala> val a: res2.S = _5 
<console>:26: error: type mismatch; 
found : shapeless.nat._5 
    (which expands to) shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]]] 
required: res2.S 
    (which expands to) shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless.Succ[shapeless._0]]]] 
     val a: res2.S = _5 
        ^

scala> res2.S 
res3: Int = 4 

Я также добавил метод для генерирования соответствующего целочисленного значения. На самом деле я не знаю существующего способа вызвать значение Nat из типа Nat (или, может быть, я просто слеп). Но я думаю, что в большинстве случаев Nat полезен только на уровне уровня, и что вам лучше работать с фактическим Int s на уровне значений.

Это также не так сложно реализовать, если вам действительно нужен уровень ценности Nat s. Но если вы посмотрите на их реализацию в бесформенном виде, они на самом деле просто пустые коробки, поэтому вы не можете с ними справиться. Полезны только их типы.

+0

Для моего собственного понимания, каково различие между 'ev.type' и' ev', за тип возврата «SumZippedProduct # apply»? –

+0

Это одноэлементный тип 'ev', но он не выводится автоматически. Поэтому, если вы явно не заявляете, что хотите использовать тип singleton как возвращаемый тип 'SumZippedProduct [L, M]', вы будете выведены, и вы потеряете информацию о типе. –

+0

Вы также можете написать как 'def apply [L <: HList, M <: HList] (неявный ev: SumZippedProduct [L, M]): SumZippedProduct.Aux [L, M, ev.S] = ev'. –