如何将类型检查添加到函数提取摘要
How to add type-checking to a function extraction summary
我正在发现 Dotty,我很想提出我的算法的类型化版本。
我想实现以下我可以在 JavaScript 中轻松完成的目标。它基本上是提取记录或数组的 属性 的一种浓缩方式:
function Field(key, action) {
return {
apply(record) {
return action.apply(record[key]);
}
};
}
var Identity = { apply(record) { return record; } };
console.log(Field(3, Field("a", Identity)).apply([0, 1, 2, {a: "Hello"}]))
// Prints out "Hello"
我有一堆函数,比如我正在尝试输入的 Field。
到目前为止,这是我尝试过的。记录或对象被建模为结构类型{ def get(k: Key): KeyMapper[Key] }
,如果输入的类型是静态已知的,它基本上试图静态获取字段的类型,如。
这是我第一次成功的尝试,下面是剩下的和失败的。
trait Apply[A, B] {
def apply(a: A): B
}
case class Identity[A]() extends Apply[A, A] {
def apply(a: A) = a
}
case class Field
[Key: ClassTag,
KeyMapper[_],
Record <: { def get(k: Key): KeyMapper[Key]},
B](key: Key, subAction: Apply[KeyMapper[Key], B]) extends Apply[Record, B] {
def apply(record: Record) = subAction(record.get(key))
}
到目前为止一切顺利,编译时没有出现类型错误。现在,我希望将类型定义 Key
和 KeyMapper
集成为记录的一部分,这样我只有两个类型参数,而不是四个,并且代码更易于维护。
我尝试了以下方法:
trait Record {
type KeyMapper[T]
type Key
def apply(k: Key): KeyMapper[Key]
}
case class Field[A <: Record, U](key: A#Key, subAction: Apply[A#KeyMapper[A#Key], U]) extends Apply[A, U] {
def apply(record: A): U = subAction(record(key))
我收到以下错误::
[error] | def apply(record: A): U = subAction(record(key))
[error] | ^^^
[error] | Found: (Down.this.key : A#Key)
[error] | Required: record.Key
好的,到目前为止,除了将 key
转换为 .asInstanceOf[record.Key]
之外,我没有看到任何其他方法,然后我收到以下错误:
[error] 43 | def apply(record: A): U = subAction(record(key.asInstanceOf[record.Key]))
[error] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error] | Found: record.KeyMapper[record.Key]
[error] | Required: A#KeyMapper[A#Key]
好吧,我有点失望,但我给 A#KeyMapper[A#Key]
添加了一个演员表。然后我得到错误:
[error] 42 | case class Field[A <: Record, U](key: A#Key, subAction: Apply[A#KeyMapper[A#Key], U]) extends Apply[A, U] {
[error] | ^
[error] | A is not a legal path
[error] | since it is not a concrete type
嗯,所以我读了一下,发现类型投影已被弃用并从 Dotty 中删除,所以我需要有一个具体的值。这是我的下一次尝试:
trait RecordAndEdit { outer =>
type Val <: {
def get(k: outer.Key): outer.Map[k.type]
}
type Key
type Map[_]
}
class Field[U](val rOps: RecordAndEdit)(val key: rOps.Key, val subAction: Apply[rOps.Map[rOps.Key], U]) extends Apply[rOps.Val, U] {
def apply(record: rOps.Val): U = subAction(record.get(key))
}
我收到错误
[error] 35 | def apply(record: rOps.Val): U = subAction(record.get(key))
[error] | ^^^^^^^^^^^^^^^
[error] |Structural access not allowed on method get because it has a method type with inter-parameter dependencies
此时,我不明白如何解决此错误消息,因为我希望 get 方法具有取决于输入类型的 return 类型。
有什么线索吗?
好的,多亏了评论,我才得以精心设计以下答案,不需要投影类型,但使用依赖类型,如 this answer:
trait Apply[Input, Output]:
def apply(k: Input): Output
trait WithDomain[X] {
type Key
type KeyMapper[_ <: Key]
def get(x: X, k: Key): KeyMapper[k.type]
}
class Field[Input, Output](using val g: WithDomain[Input])(val key: g.Key, val next: RecordEdit[g.KeyMapper[key.type], Output]) extends Apply[Input, Output]:
def apply(r: Input): Output =
next(g.get(r, key))
object Field:
def apply[Input, Output](using g: WithDomain[Input])(key: g.Key, next: RecordEdit[g.KeyMapper[key.type], Output]): RecordEdit[Input, Output] =
new Field[Input, Output]()(key, next)
class Identity[T] extends RecordEdit[T, T]:
def apply(r: T) = r
object Identity:
def apply[T]() = new Identity[T]()
一切都按预期进行,例如:
class Node(val tag: String, val children: Array[Node] = Array())
given as WithDomain[Node] { self =>
type Key = "tag" | "children"
type Mapping[X <: self.Key] = (X match {
case "tag" => String
case "children" => Array[Node]
})
def get(x: Node, k: self.Key): self.Mapping[k.type] = k match {
case _: "tag" => x.tag
case _: "children" => x.children
}
}
given[T] as WithDomain[Array[T]] {
type Key = Int
type Mapping[Int] = T
def get(x: Array[T], k: Int): T = x(k)
}
println(Field[Node, String]("children",
Field[Array[Node], String](0,
Field[Node, String]("tag", Identity())))(
Node("hello world", Array(Node("hi world")))))
请注意,我还为 Dotty 切换到了新的 indentation-style,我认为这很棒。
我正在发现 Dotty,我很想提出我的算法的类型化版本。 我想实现以下我可以在 JavaScript 中轻松完成的目标。它基本上是提取记录或数组的 属性 的一种浓缩方式:
function Field(key, action) {
return {
apply(record) {
return action.apply(record[key]);
}
};
}
var Identity = { apply(record) { return record; } };
console.log(Field(3, Field("a", Identity)).apply([0, 1, 2, {a: "Hello"}]))
// Prints out "Hello"
我有一堆函数,比如我正在尝试输入的 Field。
到目前为止,这是我尝试过的。记录或对象被建模为结构类型{ def get(k: Key): KeyMapper[Key] }
,如果输入的类型是静态已知的,它基本上试图静态获取字段的类型,如
trait Apply[A, B] {
def apply(a: A): B
}
case class Identity[A]() extends Apply[A, A] {
def apply(a: A) = a
}
case class Field
[Key: ClassTag,
KeyMapper[_],
Record <: { def get(k: Key): KeyMapper[Key]},
B](key: Key, subAction: Apply[KeyMapper[Key], B]) extends Apply[Record, B] {
def apply(record: Record) = subAction(record.get(key))
}
到目前为止一切顺利,编译时没有出现类型错误。现在,我希望将类型定义 Key
和 KeyMapper
集成为记录的一部分,这样我只有两个类型参数,而不是四个,并且代码更易于维护。
我尝试了以下方法:
trait Record {
type KeyMapper[T]
type Key
def apply(k: Key): KeyMapper[Key]
}
case class Field[A <: Record, U](key: A#Key, subAction: Apply[A#KeyMapper[A#Key], U]) extends Apply[A, U] {
def apply(record: A): U = subAction(record(key))
我收到以下错误::
[error] | def apply(record: A): U = subAction(record(key))
[error] | ^^^
[error] | Found: (Down.this.key : A#Key)
[error] | Required: record.Key
好的,到目前为止,除了将 key
转换为 .asInstanceOf[record.Key]
之外,我没有看到任何其他方法,然后我收到以下错误:
[error] 43 | def apply(record: A): U = subAction(record(key.asInstanceOf[record.Key]))
[error] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error] | Found: record.KeyMapper[record.Key]
[error] | Required: A#KeyMapper[A#Key]
好吧,我有点失望,但我给 A#KeyMapper[A#Key]
添加了一个演员表。然后我得到错误:
[error] 42 | case class Field[A <: Record, U](key: A#Key, subAction: Apply[A#KeyMapper[A#Key], U]) extends Apply[A, U] {
[error] | ^
[error] | A is not a legal path
[error] | since it is not a concrete type
嗯,所以我读了一下,发现类型投影已被弃用并从 Dotty 中删除,所以我需要有一个具体的值。这是我的下一次尝试:
trait RecordAndEdit { outer =>
type Val <: {
def get(k: outer.Key): outer.Map[k.type]
}
type Key
type Map[_]
}
class Field[U](val rOps: RecordAndEdit)(val key: rOps.Key, val subAction: Apply[rOps.Map[rOps.Key], U]) extends Apply[rOps.Val, U] {
def apply(record: rOps.Val): U = subAction(record.get(key))
}
我收到错误
[error] 35 | def apply(record: rOps.Val): U = subAction(record.get(key))
[error] | ^^^^^^^^^^^^^^^
[error] |Structural access not allowed on method get because it has a method type with inter-parameter dependencies
此时,我不明白如何解决此错误消息,因为我希望 get 方法具有取决于输入类型的 return 类型。 有什么线索吗?
好的,多亏了评论,我才得以精心设计以下答案,不需要投影类型,但使用依赖类型,如 this answer:
trait Apply[Input, Output]:
def apply(k: Input): Output
trait WithDomain[X] {
type Key
type KeyMapper[_ <: Key]
def get(x: X, k: Key): KeyMapper[k.type]
}
class Field[Input, Output](using val g: WithDomain[Input])(val key: g.Key, val next: RecordEdit[g.KeyMapper[key.type], Output]) extends Apply[Input, Output]:
def apply(r: Input): Output =
next(g.get(r, key))
object Field:
def apply[Input, Output](using g: WithDomain[Input])(key: g.Key, next: RecordEdit[g.KeyMapper[key.type], Output]): RecordEdit[Input, Output] =
new Field[Input, Output]()(key, next)
class Identity[T] extends RecordEdit[T, T]:
def apply(r: T) = r
object Identity:
def apply[T]() = new Identity[T]()
一切都按预期进行,例如:
class Node(val tag: String, val children: Array[Node] = Array())
given as WithDomain[Node] { self =>
type Key = "tag" | "children"
type Mapping[X <: self.Key] = (X match {
case "tag" => String
case "children" => Array[Node]
})
def get(x: Node, k: self.Key): self.Mapping[k.type] = k match {
case _: "tag" => x.tag
case _: "children" => x.children
}
}
given[T] as WithDomain[Array[T]] {
type Key = Int
type Mapping[Int] = T
def get(x: Array[T], k: Int): T = x(k)
}
println(Field[Node, String]("children",
Field[Array[Node], String](0,
Field[Node, String]("tag", Identity())))(
Node("hello world", Array(Node("hi world")))))
请注意,我还为 Dotty 切换到了新的 indentation-style,我认为这很棒。