Проблема, разрешающая arty функции args для обработки списка дисков, используя Shapeless

В следующем представлении есть код для идеи, которую я играю с помощью

package com.test1

import scala.language.implicitConversions
import shapeless._
import FromTraversable._
import Traversables._
import Nat._
import Tuples._

trait ToArity[P, N <: Nat]

object ToArity {
  implicit def prod1[P <: Product1[_]] = new ToArity[P, _1] {}
  implicit def prod2[P <: Product2[_, _]] = new ToArity[P, _2] {}
  // ad nauseum...
}

trait SizedHListAux[A, N <: Nat, T <: HList]

object SizedHListAux {
  implicit def base[A, H <: HList] = new SizedHListAux[A, _0, HNil] {}
  implicit def induct[A, H <: HList, N <: Nat, P <: Nat](implicit r: PredAux[N,P], k: SizedHListAux[A, P, H]) = new SizedHListAux[A, N, A :: H] {}
}

trait SomeFun {
  type Result
  def apply(): Result
}

// I want to abstract over A, the contained type in the List
// over P the Product type which is the arg notably its arity
// This means we need to recover arity of the Product type and render it in value space
// and also means that we need to compute the type of the intermediate HList
object SomeFun {
  def produce(m: SomeFun): m.Result = m()

  implicit def fromF1[T, A, P <: Product, N <: Nat, H <: HList](f1: (P => T, List[A]))(implicit k: ToArity[P, N], toI: ToInt[N], l: SizedHListAux[A, N, H], toHL: FromTraversable[H], tp: TuplerAux[H, P]) =
    new SomeFun {
      type Result = (T, List[A])
      def apply(): Result = {
        val (f, as) = f1
        val (ts, rest) = (as.take(toI()), as.drop(toI()))
        f((toHL(ts).get).tupled) -> rest
      }
    }
  // Debug Arity checker
  def printArity[P <: Product, N <: Nat](p: P)(implicit k: ToArity[P, N], toI: ToInt[N]) = println("Arity: " + toI())
}

object Test {
  val thedata = List("foo", "bar", "baz", "bob")
  val tfn = (x: (String, String)) => println("%s and %s".format(x._1, x._2))
  def foo = SomeFun.printArity("a" -> "b")
  //def doit = SomeFun.produce((tfn, thedata)) // Adding this line does not compile
}

Идея заключается в том, что вы используете аргумент аргумента функции, в данном случае, для типа продукта, для управления анализом связанного списка [A]. Вид, как использование липкой ленты для удаления слоев графена из графита, т.е. Тип функций вытащить вещи из списка. Это всего лишь эскиз с использованием одного содержащегося типа, но я предполагаю, что он может быть обобщен. Важным аспектом является то, что сами функции не знают о обработке списка.

Однако... эта концепция, похоже, терпит неудачу при попытке разрешить ToArity [P, N] неявным. Само по себе ToArity разрешимо, о чем свидетельствует printArity().

Может кто-то пролить свет на то, почему это не разрешимо в контексте fromF1? Разве он не может разрешить все зависимые импликации, а затем регистрирует ошибку с первой, т.е. N не может быть найден, чтобы удовлетворять ToArity, ToInt и SizedHListAux?

Ответ 1

Обновление: я только что видел ваше редактирование, что означает, что вы уже рассмотрели проблему, отмеченную в первых параграфах здесь, но я надеюсь, что остальное полезно.

Проблема заключается в том, что ваш экземпляр SizedHListAux не выводится:

scala> implicitly[SizedHListAux[String, _1, String :: HNil]]
<console>:25: error: could not find implicit value for parameter e...

К счастью, это легкое исправление:

object SizedHListAux {
  implicit def base[A] = new SizedHListAux[A, _0, HNil] {}
  implicit def induct[A, H <: HList, N <: Nat, P <: Nat](implicit
    r: PredAux[N, P],
    k: SizedHListAux[A, P, H]
  ) = new SizedHListAux[A, N, A :: H] {}
}

Я только что удалил параметр типа R <: PredAux[N, P] и набрал r соответствующим образом. Я также удалил параметр неиспользуемого типа H на base, хотя он не вызывал проблем - он просто ничего не делал.

Это почти все - теперь все экземпляры для fromF1 выводятся:

scala> SomeFun.fromF1((tfn, thedata))
res0: SomeFun{type Result = (Unit, List[String])} = [email protected]

Вы все равно не получите представление от типа (tfn, thedata) до SomeFun. Рассмотрим следующий упрощенный пример:

scala> trait Foo
defined trait Foo

scala> trait Bar[A, B]
defined trait Bar

scala> implicit def toInt[F <: Foo, X](f: F)(implicit ev: Bar[F, X]) = 42
toInt: [F <: Foo, X](f: F)(implicit ev: Bar[F,X])Int

scala> implicit object fooBar extends Bar[Foo, String]
defined module fooBar

scala> toInt(new Foo {})
res0: Int = 42

scala> implicitly[Foo => Int]
<console>:12: error: No implicit view available from Foo => Int.
              implicitly[Foo => Int]

Итак, хотя у нас есть неявный метод в области видимости, который преобразует Foo в Int, что X вызывает проблемы для компилятора, когда он пытается найти представление из Foo в Int.

В вашем случае я бы избегал этого ограничения, пропустив бизнес SomeFun и имея метод, который принимает (P => T, List[A]) и возвращает (T, List[A]).

Я также заметил, что как ToArity, так и SizedHListAux кажутся ненужными, так как вы можете собрать те же доказательства с TuplerAux, LengthAux и LUBConstraint. Например:

import shapeless._

trait SomeFun {
  type Result
  def apply(): Result
}

implicit def fromF1[T, A, P <: Product, N <: Nat, H <: HList](
  f1: (P => T, List[A])
)(implicit
  tp: TuplerAux[H, P],
  hl: LengthAux[H, N],
  toHL: FromTraversable[H],
  allA: LUBConstraint[H, A],
  toI: ToInt[N]
) = new SomeFun {
  type Result = (T, List[A])
  def apply(): Result = {
    val (f, as) = f1
    val (ts, rest) = (as.take(toI()), as.drop(toI()))
    f((toHL(ts).get).tupled) -> rest
  }
}

И затем:

val tfn = (x: (String, String)) => println("%s and %s".format(x._1, x._2))
val thedata = List("foo", "bar", "baz", "bob")
val sf = fromF1((tfn, thedata))

И наконец:

scala> sf()
foo and bar
res2: (Unit, List[String]) = ((),List(baz, bob))

Не нужно раздражать prodN шаблона.