Я пытаюсь описать сложный граф со многими различными типами узлов и ребер, которые могут быть связаны только друг с другом в соответствии с набором правил. Я бы хотел, чтобы эти правила проверялись во время компиляции с использованием системы типов языка. В моем реальном приложении существует множество различных типов node и edge.
Я легко создал простой пример в Scala:
sealed trait Node {
val name: String
}
case class NodeType1(override val name: String) extends Node
case class NodeType2(override val name: String) extends Node
case class NodeType3(override val name: String) extends Node
sealed trait Edge
case class EdgeType1(source: NodeType1, target: NodeType2) extends Edge
case class EdgeType2(source: NodeType2, target: NodeType1) extends Edge
object Edge {
def edgeSource(edge: Edge): Node = edge match {
case EdgeType1(src, _) => src
case EdgeType2(src, _) => src
}
}
object Main {
def main(args: Array[String]) {
val n1 = NodeType1("Node1")
val n2 = NodeType2("Node2")
val edge = EdgeType1(n1, n2)
val source = Edge.edgeSource(edge)
println(source == n1) // true
}
}
Действительный граф может связывать только данный тип ребра между указанными типами узлов, как показано в примере выше Scala. Функция "edgeSource" извлекает исходный node из края, как простой.
Здесь приведен нерабочий пример того, что я хотел бы написать в OCaml:
type node =
NodeType1 of string
| NodeType2 of string
type edge =
EdgeType1 of NodeType1 * NodeType2
| EdgeType2 of NodeType2 * NodeType1
let link_source (e : edge) : node =
match e with
| EdgeType1 (src, _) -> src
| EdgeType2 (src, _) -> src
Проблема заключается в том, что "NodeTypeX" являются конструкторами, а не типами. Следовательно, я не могу использовать их, когда описываю кортежи с источником и целью, для которых определены грани. Функция "link_source" может возвращать только один тип, а "node" - это вариант, который может что-то вернуть.
Я пытаюсь исправить это как в OCaml, так и в Haskell, и вот пример одного из них в OCaml, где тип node wraps node_type_X:
type node_type_1 = NodeType1 of string
type node_type_2 = NodeType2 of string
type node =
NodeType1Node of node_type_1
| NodeType2Node of node_type_2
type edge =
EdgeType1 of node_type_1 * node_type_2
| EdgeType2 of node_type_2 * node_type_1
let link_source (e : edge) : node =
match e with
| EdgeType1 (src, _) -> NodeType1Node src
| EdgeType2 (src, _) -> NodeType2Node src
Но проблема заключается в том, что я дублирую информацию о типе. Я указываю исходный тип node в определении edge, и он также задается при сопоставлении края в link_source как NodeTypeXNode.
Очевидно, я не понимаю, как решить эту проблему. Я застрял в иерархиях классов. Каким будет правильный способ выразить то, что я достигаю в коде Scala выше в OCaml или Haskell?