为ADT派生类型类实例,其中一些备选方案没有类型参数

时间:2017-04-02 13:44:09

标签: scala

我有一个相当复杂的ADT代表一种小的查询语言(mongodb,具体而言)。简化版看起来有点像:

sealed abstract class Query extends Product with Serializable

final case class Eq[A](field: String, value: A) extends Query
final case class And(queries: Seq[Query]) extends Query
case object None extends Query

我已声明Query没有类型参数,因为并非所有值实际上都有一个 - None,例如,无参数。

我还有一个类型类DocumentEncoder[A],可让我将A转换为BsonDocument

我遇到的问题是Query需要DocumentEncoder。为每个替代方案声明一个是相当微不足道的:

  • Eq[A]自我写作,提供A: DocumentEncoder
  • And非常相似,如果我们假设Query确实有DocumentEncoder个实例。
  • None只是编码为空的BSON文件

我所挣扎的是撰写全球DocumentEncoder[Query]。我通常做的是每个替代方案上的模式匹配,但在这种情况下,我会遇到Eq[A]:我需要表达类似case Eq[A: DocumentEncoder](field, value) => ...的内容,但这个据我所知,是不可能的 - 模式匹配在运行时发生,在编译时隐式解析。

我发现的解决方案非常不令人满意,就是将BsonEncoder[A]存储为Eq[A]的字段。这允许我写一些类似的东西:

implicit val queryEncoder: DocumentEncoder[Query] = DocumentEncoder.from {
  case e@Eq(field, value) => [...] e.encoder.encode(value) [...]
  [...]
}

我无法帮助但发现这很糟糕,但无法找到更优雅的解决方案。我唯一能想到的是我的前提(Query不应该有类型参数)是有缺陷的,但是:

  • 有一个类型参数,我该如何撰写And的类型声明?
  • 可以将None声明为Query[Unit]吗?
  • 也许在我的情况下,我总是可以通过一个类型参数逃脱,但是理论上更通用的情况呢?它不可能?

好吧,好吧,所以我可以想到另一个解决方案,但感觉有点过分:让Query类型成为类型成员而不是类型参数,并声明{{1将类型成员提升为参数的类型别名(用于隐式解析)。这种感觉就像一个大男孩的解决方案,但是 - 我已经看到它在图书馆中使用像无形的,我不知何故觉得我的代码或问题不是。还需要这种专家概念。

0 个答案:

没有答案