无形无法在通用上下文中工作

时间:2017-06-04 20:09:58

标签: scala generics shapeless

我仍然试图绕过Shapeless(并且,在较小程度上,Scala!)并且我一直在编写一些简单的代码来为案例类生成随机实例数据 - 主要基于这里的指南:{{ 3}}(该示例涵盖了JSON Writer实现)

我创建了一个Generator[A]特征并为简单类型创建了隐式实现,并且根据上面链接中的示例,我还创建了隐式实现来处理HList,HNil,Coproduct和CNil:

  implicit def hnilGenerator = new Generator[HNil] {
    override def generate(a: HNil) = HNil
  }

  implicit def hconsGenerator[H, T <: HList](implicit headGen: Generator[H], tailGen: Generator[T]) =
    new Generator[H :: T] {
      override def generate(a: H :: T) = headGen.generate(a.head) :: tailGen.generate(a.tail)
    }

  implicit def cnilGenerator: Generator[CNil] =
    new Generator[CNil] {
      override def generate(a: CNil): CNil = throw new RuntimeException("Invalid candidate configuration")
    }

  implicit def cconsGenerator[H, T <: Coproduct] =
    new Generator[H :+: T] {
      override def generate(a: H :+: T) = throw new RuntimeException("Invalid candidate configuration")
    }

我现在可以使用此代码根据案例类或密封特征生​​成随机实例:

    it("should work with a case class to hlist") {
      case class Test(x: IntGene, y: DoubleGene, z: BooleanGene)
      val c = Generic[Test].to(Test(IntGene(), DoubleGene(), BooleanGene()))
      generate(c)
    }
    it("what happens with sealed traits") {
      sealed trait Shape
      case class Square(width: Int, height: Int) extends Shape
      case class Circle(radius: Int) extends Shape

      val c = Generic[Shape].to(Circle(1))
      generate(c)
    }

但是,如果我尝试使这个通用(如在参数类型中)我得到的编译错误无法找到必要的含义,则上述两种工作都没有问题:

it("should handle generics") {
  case class GenericTest[A: Generic](x: A) {
    def convert() = {
      val c = Generic[A].to(x)
      generate(c)
    }
  }
}

所以根据我的理解,因为我使用了Generic上下文绑定A,编译器知道它将可用,所以c必须是一些可能从调用返回的to(x) - 我是否遗漏了实现中的某些内容来处理来自Generic无形调用的返回类型?或者我误解了什么?

我希望这是可能的,我错过了一些东西 - 是编译器不知道将传入的内容(我假设没有),或者是否有另一种需要隐式处理的可能类型从那个to(x)电话?

修改

下面添加了编译错误 - 我真的只是想了解:是否有一些来自to(x)无形调用的返回情况我还没有提供,或者是因为编译器没有& #39;知道会传入什么内容并且有些类型没有被照顾(例如我没有隐含添加日期生成器 - 并且案例类可能包含任何类型?我希望那是不是这样,并且由于编译器知道实际上没有传递给类/方法,它知道没有问题吗?)

GeneratorSpec.scala:44: could not find implicit value for parameter gen: io.github.robhinds.genotype.Generator[GenericTest.this.evidence$1.Repr]
          generate(c)

我的generate方法只是一个简单的辅助方法,它被赋予隐式Generator

def generate[A](a: A)(implicit gen: Generator[A]) = gen.generate(a)

2 个答案:

答案 0 :(得分:3)

您的问题来自于Generic 将案例类转换为HList并将密封特征转换为{{1 (不是递归地)。

因此,如果您使用通用Coproduct,则表示您没有关于GenericHList给出的信息,因此,您无法使用产品和联合产品规则来查找所需内容隐式的。在某些明确的情况下,您可能会遇到同样的问题,因此我将以此为例:

假设你有一个案例类架构

Coproduct

隐式case class Bottom(value: String) case class Top(bot: Bottom, count: Int) Generic[Top]作为输出类型,因此您将要求隐式type MyHList = Bottom :: Int :: HNil。但由于您在范围内没有隐式Generator[MyHList],因此您将无法使用Generator[Bottom]

在一般情况下,情况更糟。编译器只能推断hconsgenerator输出类型为HList(假设您忘记了Generic[A]),因此您需要隐式Coproduct,这是您无法提供的。

解决方案是为具有可以自己生成的泛型的构造提供隐式:

Generator[HList]

修改

您现在可以按照implicit def generic2generate[T, L <: HList](implicit generic: Generic.Aux[T, L], lGen: Generator[L]): Generator[T] = new Generator[T] { def generate(c: T) = generic.from(lGen.generate(generic.to(c))) } 类型的隐式解决方案:

  • 我们可以使用最后一条规则Top,如果某个Generator[Top]有一个Generic.Aux[Top, L],那么L

  • 隐含存在的唯一Generator[L]Generic.Aux[Top, _],因此我们沦为Generic.Aux[Top, Bottom :: Int :: HNil]

  • 使用hcons规则三次,我们将简化为Generator[Top, Bottom :: Int :: HNil]Generator[Bottom]Generator[Int]

  • Generator[HNil]已给出(我假设)且Generator[Int]是第一条规则,因此我们沦为Generator[HNil]

  • 唯一可以提供一条规则的规则又是第3条规则,因此我们必须找到Generator[Bottom],因为唯一可用的Generator[String :: HNil]Generic。< / p>

  • 使用hcons规则,我们要找到一个Generic.Aux[Bottom, String :: HNil],这很容易提供。

此示例显示了不同的观点:

  • 首先,编译时可能需要很长时间来解决所有这些含义(我只给出了证明的要点,但编译器必须尝试所有可能的分支)

  • 第二,此解决方案只能针对特定 Generator[String]进行,但无法推断一般(尽管这似乎可能是反...直观的);即使人类的头脑能够证明它适用于每个 Generic,编译器也无法处理它。

答案 1 :(得分:0)

我认为你错过了Generator类型绑定。

POST