1

As a follow-up to Checksum at the Type Level using Shapeless, I attempted a re-write:

package net

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

// Given an HList of size 3, provide evidence of the sum of HList
// multiplied by _3 :: _2 :: _1 :: HNil
// Example: input: _1 :: _2 :: _2 -> output: _3 + _4 + _2 :: HNil
trait HListProductZipped[L <: HList] {
  type Out <: HList
}
object HListProductZipped {

  type Aux[L <: HList, Out1 <: HList] = HListProductZipped[L] { type Out = Out1 }

  def apply[L <: HList](implicit ev: HListProductZipped[L]): Aux[L, ev.Out] = ev

  implicit def induct[H <: Nat, T <: HList, L <: Nat](
    implicit ev: Length.Aux[H :: T, L],
             prod: Prod[H, L],
             rest: HListProductZipped[T]
  ): HListProductZipped.Aux[H :: T, prod.Out :: rest.Out] = new HListProductZipped[H :: T] {
    type Out = prod.Out :: rest.Out
  }

  implicit val hnilHListProductZipped: HListProductZipped.Aux[HNil, HNil] = new HListProductZipped[HNil] {
    type Out = HNil
  }

}

trait HListSum[L <: HList] {
  type Out <: Nat
}
object HListSum {

  type Aux[L <: HList, O <: Nat] = HListSum[L] { type Out = O }

  def apply[L <: HList](implicit ev: HListSum[L]): Aux[L, ev.Out] = ev

  implicit def hListSumInductive[H <: Nat, L <: HList, S <: Nat, TS <: Nat](
    implicit rest: HListSum.Aux[L, S],
             sum: Sum.Aux[H, S, TS]): HListSum.Aux[H :: L, TS] = new HListSum[H :: L] {
    type Out = sum.Out
  }

  implicit val hlistSumHNil: HListSum.Aux[HNil, _0] = new HListSum[HNil] {
    type Out = _0
  }
}


//  account number:  3  4  5  8  8  2  8  6  5
//  position names:  d9 d8 d7 d6 d5 d4 d3 d2 d1
//
//  checksum calculation:
//    (d1+2*d2+3*d3 +..+9*d9) mod 11 = 0
trait IsValidInductive[L <: HList]
object IsValidInductive {
  def apply[L <: HList](implicit ev: IsValidInductive[L]) = ev

  implicit def wholeIsValid[H <: Nat, T <: HList, L <: HList, S <: Nat](
   implicit length: Length.Aux[H :: T, _9],
            ev: HListProductZipped.Aux[H :: T, L],
            sum: HListSum.Aux[L, S],
            mod: Mod.Aux[S, _11, _0]
            ): IsValidInductive[H :: T] = new IsValidInductive[H :: T] {}

}

It works for simple cases:

scala> IsValidInductive[_0 :: _0 :: _0 :: _0 :: _0 :: _0 :: _0 :: _0 :: _0 :: HNil]
// compiles since 0 mod 11 == 0

scala> IsValidInductive[_1 :: _0 :: _0 :: _0 :: _0 :: _0 :: _0 :: _0 :: _2 :: HNil]
// compiles since (9 + 2) mod 11 == 0

scala> IsValidInductive[_1 :: _0 :: _0 :: _0 :: _0 :: _0 :: _0 :: _0 :: _1 :: HNil]
error: could not find implicit value, since 9 mod 11 == 2, not 0

But, after a few hours (I don't know since I ran the command, then left for a few hours), I saw a StackOverflowError:

scala> IsValidInductive[_3 :: _4 :: _5 :: _8 :: _8 :: _2 :: _8 :: _6 :: _5 :: HNil]
java.lang.StackOverflowError
    at scala.reflect.internal.tpe.TypeMaps$TypeMap.mapOver(TypeMaps.scala:110)
    at scala.reflect.internal.tpe.TypeMaps$FilterTypeCollector.traverse(TypeMaps.scala:1058)

Note that I'm using shapeless's SNAPSHOT, which includes the following Improved compile times for Sum, Diff and Prod.

Community
  • 1
  • 1
Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384
  • 1
    One suggestion: try to keep the values you're doing arithmetic on as small as possible—e.g. by modding as you sum. This is kind of unsatisfying since it prevents you from breaking things up into nicely compositional pieces, but if you want the code to compile in your lifetime it's currently your best bet. – Travis Brown Feb 27 '17 at 17:57
  • Thanks, Travis. I don't know off-hand how to break it down, but maybe http://math.stackexchange.com/questions/2165315/distributing-modulo will help me. – Kevin Meredith Feb 28 '17 at 13:12

0 Answers0