来自节点字符串的树构造(无法理解此Haskell代码)

时间:2017-10-18 13:23:14

标签: haskell

我试图通过99个Haskell问题学习Haskell并遇到“4问题70”。它是关于“从节点字符串构建树”。 (描述为here

我只是不明白最后的解决方案there

  

这个练习真的是为Prolog设计的,需要一个双向谓词。在Haskell中最接近的是一堂课!这实际上是一个解析/漂亮的打印解决方案,名称匹配。我们使用显式指令列表,而不是尝试使用字符串和特殊的“向上”字符(在类型级别上不起作用)。请求的谓词是BuildTree。

{-# language MultiParamTypeClasses, FunctionalDependencies,
         FlexibleContexts, FlexibleInstances, UndecidableInstances,
         DataKinds, PolyKinds #-}

import Data.Tree
data Instr c = Up | Down c

class ParseTree str t '[]
  => BuildTree (str :: [Instr c]) (t :: Tree c) | str -> t, t -> str
instance ParseTree str t '[]
     => BuildTree str t
  

我们使用两个助手来实现它:

class ParseTree (str :: [Instr c]) (t :: Tree c) (rem :: [Instr c])
            | str -> t rem, t rem -> str
class ParseForest (str :: [Instr c]) (ts :: [Tree c]) (rem :: [Instr c])
            | str -> ts rem, ts rem -> str

instance ParseTree ('Down c ': 'Up ': r) ('Node c '[]) r
instance ParseForest ('Down d ': is) (t ': ts) r
     => ParseTree ('Down c ': 'Down d ': is) ('Node c (t ': ts)) r

instance ParseForest ('Up ': is) '[] is
instance ( ParseTree ('Down c ': is) t r
     , ParseForest r ts r')
     => ParseForest ('Down c ': is) (t ': ts) r'

我ghci这段代码,找不到准备好使用的功能。

  

请求的谓词是BuildTree。

这是否意味着我必须编写这个谓词?

谢谢你的帮助!

1 个答案:

答案 0 :(得分:4)

首先,我同意这些评论:在你使用Haskell几年之前,不要试图理解这个“解决方案”。但是,为了完整起见,这里是如何使用它......

此解决方案实现为在编译时发生的“类型级计算”。我有点惊讶,作者没有提供如何使用它的解释,因为它远非显而易见。

可能有更好的方法,但这是一个有效的方法。第一:

  • TypeOperator添加到扩展名列表中 - 如果没有它,我无法在GHC 8.0.2下编译。
  • 添加import Data.Proxy

然后,在底部添加:

data Nodes = A | B | C | D | E
u = (Proxy :: BuildTree str (Node A [Node B '[], Node C '[]]) => Proxy str)

这定义了一些节点标签的构造函数,这些构造函数被DataKinds扩展名提升为类型。然后,我们将符号u定义为str类型的值,该值受限于基于“谓词”BuildTree str t的约束,其中t是树。请注意u并不重要(实际上它是无聊的值“Proxy”);它是u类型,这很重要......

Haskell的类型推断将计算我们可以在GHCi中显示的str类型:

> :t u
u :: Proxy '['Down 'A, 'Down 'B, 'Up, 'Down 'C, 'Up, 'Up]
>

同样,添加:

v = (Proxy :: BuildTree (['Down 'A, 'Down 'B, 'Up, 'Down 'C, 'Up, 'Up]) t 
    => Proxy t)
底部的

为我们提供了逆向转换:

> :t v
v :: Proxy ('Node 'A '['Node 'B '[], 'Node 'C '[]])
>

请注意,此处使用Proxy只是为了允许我们将类型分配给对象。如果没有代理,我们会尝试将uv分配给*以外的类型(即具体类型),这是不允许的。