如何去除实现封闭特质的重复case语句

5
我经常发现,在对每个实现执行相同操作之前,我需要提取密封特质的类型:
sealed trait Trait
case class Foo() extends Trait
case class Bar() extends Trait
// ... lots of other implementations

// *must* take a `Trait`, not a `T <: Trait`
def thing(t: Trait): ??? = t match {
  case f: Foo => // something with the instance and specific type
  case b: Bar => // something with the instance and specific type 
  // ... same thing again for other implementations
}

举个例子。
// typically provided by somebody else...
trait Thing[T] { def thingy: String }
implicit def thing[T]: Thing[T] = new Thing[T] { def thingy = "stuff" }

def thing(t: Trait): String = t match {
  case Foo() => implicitly[Thing[Foo]].thingy
  case Bar() => implicitly[Thing[Bar]].thingy
  // ...
}

我希望能减少在这方面所涉及的样板代码。


2
这个问题并没有很清楚地说明为什么你需要使用shapeless作为解决方案。根据问题的描述,def thing[A <: Trait: Thing](a: A)似乎已经足够了。 - Michael Zajac
API必须是def thing(t: Thing)而不是def thing[T <: Thing](t: T) - fommil
你是否发现单个构造函数的实例运行良好?对我来说,它仍然似乎非常错误,但我不知道这是否只是反子类型偏见。 - Travis Brown
@TravisBrown 你的意思是每个T都有一个Thing实例吗?这并不会改变问题的定义,因为我仍然需要处理大量的模式匹配,并在每个右侧使用相同的代码。 - fommil
没错,我(离题了)是在询问你对这种方法的一般经验 - 最好在其他地方讨论。 - Travis Brown
当我编写自定义编组器或实现TypeClass模式时,这对我来说经常出现。 - fommil
1个回答

4

更新:现在我们可以使用shapeless的类型类推导。例如:https://github.com/fommil/shapeless-for-mortals

事实证明,您可以使用shapeless的多态函数和共同产品来完成此操作:

  object thing extends Poly1 {
    implicit def action[T <: Trait: Thing] = at[T](
      a => implicitly[Thing[T]].thingy
    )
    // magic that makes it work at the sealed trait level
    def apply(t: Trait): String =
      Generic[Trait].to(t).map(thing).unify
  }

这样就可以像使用

println(thing(Foo(): Trait))

我希望通过一个抽象类来简化编写(暂时不考虑将隐式参数传递给action),例如:

abstract class MatchSealed[In, Out] extends Poly1 {
  implicit def poly[T <: In] = at[T](action)

  def action[T <: In](t: T): Out

  import ops.coproduct.Mapper
  def apply[R <: HList](in: In)(
    implicit
      g: Generic.Aux[In, R],
      m: Mapper[this.type, R]
  ): Out = {
    val self: this.type = this
    g.to(in).map(self).unify
  }
}

但是,在最后一行出现了缺少Mapper[self.type, g.Repr]的错误。我不知道缺少哪个隐式参数,但我怀疑是self.type。我真的想捕获realisedSelf.type,但我不知道该如何做。
更新:结果发现无法获取Mapper,因为它需要访问实现的object无法在HList上进行映射

@milessabin,你能想到任何将apply逻辑抽象成一个抽象类的方法吗?我尝试过了,但失败了,因为.map没有为任何旧的Generic定义,而我需要一些证据表明我不知道如何编码。 - fommil
1
apply[T](t: T) 需要一个隐式的 Generic 和一个将 thing 映射到表示类型上的隐式 Mapper - Miles Sabin
我本来希望将这些东西作为抽象类的隐式参数,以避免重新计算,但那也可以。 - fommil
@MilesSabin 的 Mapper 接受两个类型参数。第一个是 Poly(因此我假设它应该是 Generic[Thing]),但第二个参数 extends Coproduct 是什么? - fommil
1
@LandlockedSurfer 这种方法已经过时了,请使用链接演讲中的方法。 - fommil
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接