Scala类型系统,通过自己类型

时间:2015-07-17 08:00:20

标签: scala types

这里不太确定标准术语,因此我会尝试描述我尝试做的事情。如果您有点好奇,我实际尝试编写的应用程序是一个类似于Resque或rq的异步任务队列。

我有TaskDef[ArgsT <: AnyVal, ResultT <: AnyVal]类型。如果你有点好奇,TaskDef表示&#34;如何执行一个异步任务,它接受参数类型ArgsT和结果类型ResultT,或者任务背后的代码& #34;

我尝试定义类型TaskInst[DefT <: TaskDef]。如果您感到好奇,TaskInst代表&#34; TaskDef以及与之一起运行的相关参数,或者,一个实际的任务实例被提交到队列&#34;。 TaskInst有两个成员,definition: DefTarguments,其类型我无法用代码编写。

在英语中,我想要的约束是:&#34;对于给定的DefT,其中DefT是某些TaskDef[ArgsT, ResultT]TaskInst[DefT]应包含DefTArgsT&#34;。也就是说,任务定义的参数类型应该与赋予任务的参数类型相匹配。

如何在Scala类型系统中表达这一点?

或者,我是否错误地为我的域建模并尝试做一些非惯用的事情?一些替代方法会更加惯用吗?

提前致谢!

编辑:

我认为我的历史自我写作Java可能会在这一点上使用未经检查的演员表。对于一些未经检查的强制转换,这绝对是可行的,只是省略TaskInst参数的类型与嵌入的TaskDef参数的类型之间的约束。但是,我确实想知道这是否是编译器可以执行的,并且希望没有太可怕的语法。

2 个答案:

答案 0 :(得分:7)

将它们定义为抽象类型:

```{r}
g
```

然后使用类型投影:

trait TaskDef {
    type Arguments <: AnyVal
    type Result <: AnyVal
}

Live Demo

答案 1 :(得分:0)

@rightfold给出的答案的附加内容:

如果您希望始终使用类型参数,则需要将类型参数正确传递给类型构造函数。

对不起,我这样说有点含糊不清,所以让我用我当前的代码作为一个具体的例子。

trait TaskDef[ArgT_, ResT_] {
  type ArgT = ArgT_
  type ResT = ResT_
  val name: String
  def exec(arg: ArgT): String \/ ResT
}

class TaskInst[ArgT, ResT, DefT <: TaskDef[ArgT, ResT]] (
  val id: UUID,
  val defn: DefT,
  val arg: ArgT
)

我当前代码与@ rightfold示例的主要区别在于TaskDef采用类型参数。然后,当TaskInst的声明引用TaskDef时,它必须为类型构造函数提供适当的类型。

我最初错误地传递了占位符。也就是说,我写的是class TaskInst[DefT <: TaskDef[_, _]。事实证明,这并不意味着我的意思。 (我不知道。也许其他人可能会倾向于遵循同样的思路。所以,这只是警告而不是。)不要这样做,因为那时scalac会解释期望表示生成的占位符(正如您可能想象的那样,没有任何匹配项),然后您会得到一条模糊的错误消息,如下所示。

[error] /Users/mingp/Code/scala-redis-queue/src/main/scala/io/mingp/srq/core/TaskInst.scala:8: type mismatch;
[error]  found   : TaskInst.this.arg.type (with underlying type _$1)
[error]  required: _$1
[error]   val arg: DefT#ArgT_
[error]       ^
[error] one error found

只是张贴这篇文章,希望未来的读者不会陷入同样的​​困境。

编辑:

作为进一步的补充,既然我已经尝试了一天,我的印象是,这对于异步任务来说实际上并不是一个好的数据模型。你最好不要合并,因为TaskDef的独立实例并不是真的有用。