在 Haskell 中,为递归树创建数据类型很简单,就像我们在 XML 文档中所拥有的那样:
data XML =
Text String -- Text of text node
| Elem String [XML] -- Tagname; Child nodes
及其相关的折叠:
-- Simple fold (Child trees don't see the surrounding context.)
foldt :: (String -> a) -> (String -> [a] -> a) -> XML -> a
foldt fT fE (Text text) = fT text
foldt fT fE (Elem tagname ts) = fE tagname (map (foldt fT fE) ts)
-- Threaded fold for streaming applications.
-- See http://okmij.org/ftp/papers/XML-parsing.ps.gz
foldts :: (a -> String -> a) -> (a -> String -> a) -> (a -> a -> String -> a) -> a -> XML -> a
foldts fT fE_begin fE_end = go
where
go seed (Text text) = fT seed text
go seed (Elem tag children) =
let seed' = fE_begin seed tag in
let seed'' = foldl go seed' children in
fE_end seed seed'' tag
我现在的问题是我不知道如何为我的树数据类型添加一些额外的限制以对 HTML 进行建模。在 HTML 中,每个元素节点只能出现在正确的上下文中,并且每个元素对应于其子元素的不同上下文。例如:
- 像img这样的void 元素有一个空的上下文模型,并且不允许有任何子元素。
- 具有 Text 内容模型的元素,例如title,只能将文本节点作为子节点(不允许嵌套标签)
- div元素不能出现在 Phrasing 上下文中,因此不允许作为span元素的后代。
所以我的问题是:
在 Haskell98 中我需要做什么来模拟这些限制?(我问这个是因为我猜 Haskell98 模型应该更好地翻译成其他编程语言)
我想我们可能必须为不同的上下文创建许多不同的数据类型,但我不知道如何以有原则和清晰的方式做到这一点。我怎样才能在不迷路的情况下做到这一点,折叠功能最终会是什么样子?
如果允许我们使用现代 GHC 功能(例如 GADT),模型会是什么样子?
我有一种预感 GADT 可能有助于将限制推入类型检查器,保持折叠功能简单,但我对它们没有太多经验......
我不需要 100% 有效的解决方案,因为这显然超出了 Stack Overflow 讨论的范围。我只需要能够更好地掌握 GADT 之类的东西,并且能够自己完成剩下的工作。