【问题标题】:Issue resolving arity of function args to drive list processing, using Shapeless使用 Shapeless 解决函数 args 以驱动列表处理的问题
【发布时间】:2012-12-17 01:05:58
【问题描述】:

以下要点包含我正在玩的一个想法的代码

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
}

这个想法是您使用函数的参数 arity(在本例中为 Product 类型的 arity)来驱动关联 List[A] 的解析。有点像使用胶带从石墨上剥离石墨烯层,即功能的类型将事物从列表中拉出。这只是一个使用单一包含类型的草图,但我想它可以被概括。重要的方面是函数本身不知道 List 处理。

但是......当试图解决 ToArity[P,N] 隐式时,这个概念似乎失败了。正如 printArity() 所证明的那样,ToArity 本身是可解析的。

有人能解释一下为什么这在 fromF1 的上下文中无法解决吗?是不是它不能解决所有的依赖隐式然后用第一个注册错误,即找不到满足To​​Arity、ToInt和SizedHListAux的N?

【问题讨论】:

    标签: scala shapeless


    【解决方案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 &lt;: PredAux[N, P] 类型参数并正确键入了r。我还在base 上删除了未使用的类型参数H,即使它没有引起问题——它只是没有做任何事情。

    这几乎是全部——现在fromF1 的所有实例都得到了推断:

    scala> SomeFun.fromF1((tfn, thedata))
    res0: SomeFun{type Result = (Unit, List[String])} = SomeFun$$anon$1@7eacbeb
    

    不过,您仍然无法获得从 (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 在尝试查找从FooInt 的视图时会给编译器带来问题.

    在您的情况下,我会通过跳过 SomeFun 业务并使用一个采用 (P =&gt; T, List[A]) 并返回 (T, List[A]) 的方法来避免此限制。

    我还会注意到ToAritySizedHListAux 似乎都没有必要,因为您可以使用TuplerAuxLengthAuxLUBConstraint 收集相同的证据。例如:

    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 样板。

    【讨论】:

    • 啊!现在有道理了,感谢有关使用标准 Shapeless 组件的指针...希望我可以通过 SomeFun 间接抽象不同的 product() 变体,但现在这似乎有点过头了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-04
    • 1970-01-01
    相关资源
    最近更新 更多