【问题标题】:Write a Haskell interpreter in Haskell用 Haskell 写一个 Haskell 解释器
【发布时间】:2009-09-18 17:19:49
【问题描述】:

一个经典的编程练习是用 Lisp/Scheme 编写一个 Lisp/Scheme 解释器。可以利用完整语言的强大功能为该语言的子集生成解释器。

Haskell 有类似的练习吗?我想使用 Haskell 作为引擎来实现 Haskell 的一个子集。当然可以完成,但是有什么在线资源可以查看吗?


这是背景故事。

我正在探索使用 Haskell 作为一种语言来探索我正在教授的 Discrete Structures 课程中的一些概念的想法。这个学期我选择了Miranda,这是一种启发了 Haskell 的小语言。 Miranda 做了大约 90% 我希望它做的事情,但 Haskell 做了大约 2000%。 :)

所以我的想法是创建一种语言,它完全具有我想要的 Haskell 功能,并且不允许其他所有功能。随着学生的进步,一旦他们掌握了基础知识,我就可以选择性地“开启”各种功能。

教学“语言水平”已成功用于教授JavaScheme。通过限制他们可以做的事情,您可以防止他们在他们仍在掌握您尝试教授的语法和概念时自欺欺人。并且您可以提供更好的错误消息。

【问题讨论】:

  • 我有一个 WIP Haskell 方言,以在 Haskell 中键入 Haskell 作为基础。这里有一个演示 chrisdone.com/toys/duet-delta 它还没有准备好公开开源发布,但如果有兴趣我可以与你分享源代码。

标签: haskell functional-programming interpreter


【解决方案1】:

我喜欢你的目标,但这是一项艰巨的任务。一些提示:

  • 我在 GHC 上工作过,你不想要任何部分的源代码。 Hugs 是一个更简单、更简洁的实现,但不幸的是它是用 C 语言实现的。

  • 这只是拼图中的一小部分,但 Mark Jones 写了一篇漂亮的论文,名为 Typing Haskell in Haskell,这将是您前端的一个很好的起点。

祝你好运!确定 Haskell 的语言水平以及来自课堂的支持性证据,将对社区大有裨益,而且绝对是可发表的结果!

【讨论】:

【解决方案2】:

有一个完整的 Haskell 解析器:http://hackage.haskell.org/package/haskell-src-exts

一旦你解析了它,剥离或禁止某些东西就很容易了。我这样做是为了 tryhaskell.org 禁止导入语句、支持顶级定义等。

只需解析模块:

parseModule :: String -> ParseResult Module

那么你就有了一个模块的 AST:

Module SrcLoc ModuleName [ModulePragma] (Maybe WarningText) (Maybe [ExportSpec]) [ImportDecl] [Decl]    

Decl 类型广泛:http://hackage.haskell.org/packages/archive/haskell-src-exts/1.9.0/doc/html/Language-Haskell-Exts-Syntax.html#t%3ADecl

您需要做的就是定义一个白名单——哪些声明、导入、符号、语法可用,然后遍历 AST 并在您不希望他们知道的任何内容上抛出“解析错误”的呢。您可以使用附加到 AST 中每个节点的 SrcLoc 值:

data SrcLoc = SrcLoc
     { srcFilename :: String
     , srcLine :: Int
     , srcColumn :: Int
     }

无需重新实现 Haskell。如果你想提供更友好的编译错误,只需解析代码,过滤它,发送给编译器,解析编译器输出。如果它是“无法将预期类型 a 与推断的 a -> b 匹配”,那么您知道它可能是函数的参数太少了。

除非你真的想花时间从头开始实现 Haskell 或弄乱 Hugs 的内部结构,或者一些愚蠢的实现,我认为你应该只过滤传递给 GHC 的内容。这样,如果您的学生想要使用他们的代码库并将其带到下一步并编写一些真正成熟的 Haskell 代码,那么过渡是透明的。

【讨论】:

    【解决方案3】:

    您想从头开始构建您的解释器吗?从实现更简单的函数式语言开始,例如 lambda 演算或 lisp 变体。对于后者,有一本相当不错的 wikibook,名为 Write yourself a Scheme in 48 hours,对解析和解释技术进行了冷静而实用的介绍。

    手动解释 Haskell 会复杂得多,因为您必须处理高度复杂的特性,例如类型类、极其强大的类型系统(类型推断!)和惰性求值(归约技术)。

    所以你应该定义一个相当小的 Haskell 子集来使用,然后可以从逐步扩展 Scheme-example 开始。

    加法:

    请注意,在 Haskell 中,您可以完全访问解释器 API(至少在 GHC 下),包括解析器、编译器,当然还有解释器。

    要使用的包是hint (Language.Haskell.*)。不幸的是,我既没有找到这方面的在线教程,也没有自己尝试过,但它看起来很有希望。

    【讨论】:

    • 请注意,类型推断实际上是一个非常简单的 20-30 行算法。它的简单是美丽的。惰性评估也不难编码。我想说困难在于疯狂的语法、模式匹配以及语言中的大量内容。
    • 有趣 - 你能发布类型推断算法的链接吗?
    • 是的,看看这本免费的书 - cs.brown.edu/~sk/Publications/Books/ProgLangs/2007-04-26 - ,它在第 273 页(pdf 的第 289 页)。 alg 伪代码在 P296 上。
    • 在“The Implementation of Functional Programming Languages”中还有一个(?)类型推断/检查算法的实现。
    • 使用类型类的类型推断并不简单。
    【解决方案4】:

    创建一种完全具有我想要的 Haskell 功能的语言,并且禁止其他所有功能。随着学生的进步,一旦他们掌握了基础知识,我就可以选择性地“开启”各种功能。

    我建议使用更简单的(因为涉及的工作更少)解决这个问题。与其创建一个可以关闭功能的 Haskell 实现,不如用一个程序包装一个 Haskell 编译器,该程序首先检查代码没有使用您不允许的任何功能,然后使用现成的编译器对其进行编译。

    这类似于HLint(也有点相反):

    HLint(前身为 Haskell 博士)阅读 Haskell 程序并提出更改建议,以期使它们更易于阅读。 HLint 还可以轻松禁用不需要的建议,并添加您自己的自定义建议。

    • 实施您自己的 HLint“建议”,不要使用您不允许的功能
    • 禁用所有标准 HLint 建议。
    • 首先让您的包装器运行修改后的 HLint
    • 将 HLint 建议视为错误。也就是说,如果 HLint “抱怨”,则程序不会进入编译阶段

    【讨论】:

      【解决方案5】:

      Baskell 是一个教学实现,http://hackage.haskell.org/package/baskell

      您可以从选择要实现的类型系统开始。这和 Scheme 的解释器一样复杂,http://hackage.haskell.org/package/thih

      【讨论】:

        【解决方案6】:

        EHC 系列编译器可能是最好的选择:它正在积极开发,似乎正是您想要的 - 一系列小型 lambda 演算编译器/解释器,最终在 Haskell '98 中达到顶峰。

        但您也可以查看 Pierce 的类型和编程语言中开发的各种语言,或 Helium 解释器(供学生使用的残废的 Haskell http://en.wikipedia.org/wiki/Helium_(Haskell))。

        【讨论】:

          【解决方案7】:

          如果您正在寻找易于实现的 Haskell 子集,您可以取消类型类和类型检查。如果没有类型类,您不需要类型推断来评估 Haskell 代码。

          我为 Code Golf 挑战写了self-compiling Haskell subset compiler。它在输入上采用 Haskell 子集代码并在输出上生成 C 代码。很抱歉没有更易读的版本;在使其自编译的过程中,我手动解除了嵌套定义。

          对于有兴趣为 Haskell 子集实现解释器的学生,我建议从以下功能开始:

          • 延迟评估。如果解释器在 Haskell 中,您可能无需为此做任何事情。

          • 具有模式匹配参数和保护的函数定义。只需担心变量、缺点、nil 和 _ 模式。

          • 简单的表达式语法:

            • 整数字面量

            • 字符字面量

            • [](无)

            • 函数应用(左关联)

            • 中缀:(缺点,右结合)

            • 括号

            • 变量名

            • 函数名称

          更具体地说,编写一个可以运行它的解释器:

          -- tail :: [a] -> [a]
          tail (_:xs) = xs
          
          -- append :: [a] -> [a] -> [a]
          append []     ys = ys
          append (x:xs) ys = x : append xs ys
          
          -- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
          zipWith f (a:as) (b:bs) = f a b : zipWith f as bs
          zipWith _ _      _      = []
          
          -- showList :: (a -> String) -> [a] -> String
          showList _    []     = '[' : ']' : []
          showList show (x:xs) = '[' : append (show x) (showItems show xs)
          
          -- showItems :: (a -> String) -> [a] -> String
          showItems show []     = ']' : []
          showItems show (x:xs) = ',' : append (show x) (showItems show xs)
          
          -- fibs :: [Int]
          fibs = 0 : 1 : zipWith add fibs (tail fibs)
          
          -- main :: String
          main = showList showInt (take 40 fibs)
          

          类型检查是 Haskell 的一个重要特性。然而,从一无所有到类型检查的 Haskell 编译器是非常困难的。如果您从为上述内容编写解释器开始,向其中添加类型检查应该不那么令人生畏。

          【讨论】:

          • "延迟评估。如果解释器在 Haskell 中,您可能无需为此做任何事情。"这可能不是真的。有关在 Haskell 中实现惰性解释器的更多信息,请参阅 haskell.org/wikiupload/0/0a/TMR-Issue10.pdf 中的 Naylor 文章。
          【解决方案8】:

          您可能会查看 Happy(Haskell 中类似 yacc 的解析器),它有一个 Haskell 解析器。

          【讨论】:

            【解决方案9】:

            This 可能是个好主意 - 在 Haskell 中制作一个小版本的 NetLogo。 Here 是微型解释器。

            【讨论】:

            • 链接已失效。这个内容有没有可能在其他地方仍然存在?我会很好奇...
            • 嗯,这是一篇博文,我不知道用什么关键字来搜索它。提供链接时包含更多实质性信息的好教训...
            • 谷歌搜索“netlogo haskell”出现了......这个问题。总之,没什么大不了的。谢谢!
            【解决方案10】:

            看看helium 是否会比标准的haskell 成为更好的基础。

            【讨论】:

              【解决方案11】:

              Uhc/Ehc 是一系列启用/禁用各种 Haskell 功能的编译器。 http://www.cs.uu.nl/wiki/Ehc/WebHome#What_is_UHC_And_EHC

              【讨论】:

                【解决方案12】:

                有人告诉我Idris 有一个相当紧凑的解析器,不确定它是否真的适合修改,但它是用 Haskell 编写的。

                【讨论】:

                  【解决方案13】:

                  Andrej Bauer 的Programming Language Zoo 有一个纯函数式编程语言的小实现,有点厚颜无耻地命名为“minihaskell”。大约700行OCaml,非常容易消化。

                  该站点还包含 ML 风格、Prolog 风格和 OO 编程语言的玩具版本。

                  【讨论】:

                    【解决方案14】:

                    你不觉得用the GHC sources 去掉你不想要的东西比从头开始编写你自己的Haskell 解释器更容易吗?一般来说,与创建/添加功能相比,删除功能所涉及的工作量应该很多

                    无论如何,GHC 都是用 Haskell 编写的,所以从技术上讲,这与您对用 Haskell 编写的 Haskell 解释器的问题有关。

                    将整个东西静态链接然后只分发您定制的 GHCi 可能不会太难,这样学生就无法加载其他 Haskell 源模块。至于阻止它们加载其他 Haskell 目标文件需要做多少工作,我不知道。如果您的班级中有很多作弊者,您可能也想禁用 FFI :)

                    【讨论】:

                    • 这并不像听起来那么容易,因为许多功能都依赖于其他功能。但也许 OP 只是不想导入 Prelude 而是提供他自己的。您看到的大多数 Haskell 都是普通功能,而不是运行时的特定功能。 (当然,很多。)
                    【解决方案15】:

                    之所以有这么多 LISP 解释器,是因为 LISP 基本上是 JSON 的前身:一种对数据进行编码的简单格式。这使得前端部分很容易处理。与此相比,Haskell,尤其是语言扩展,并不是最容易解析的语言。 这些是一些听起来很难正确的句法结构:

                    • 具有可配置优先级、关联性和固定性的运算符,
                    • 嵌套的 cmets
                    • 布局规则
                    • 模式语法
                    • do- 阻塞和脱糖到一元代码

                    除了可能的操作符之外,这些中的每一个都可以由学生在完成编译器构建课程后解决,但这会分散 Haskell 实际工作的重点。除此之外,您可能不想直接实现 Haskell 的所有语法结构,而是实现 pass 以摆脱它们。这将我们带到了问题的字面核心,完全是双关语。

                    我的建议是为Core 实现类型检查和解释器,而不是完整的 Haskell。这两项任务本身已经相当复杂。 这种语言虽然仍然是一种强类型的函数式语言,但在优化和代码生成方面处理起来并不复杂。 但是,它仍然独立于底层机器。 因此,GHC 使用它作为中介语言,并将 Haskell 的大多数语法结构翻译成它。

                    此外,您不应回避使用 GHC(或其他编译器)的前端。 我不认为这是作弊,因为自定义 LISP 使用主机 LISP 系统的解析器(至少在引导期间)。清理 Coresn-ps 并将它们与原始代码一起展示给学生,应该可以让您概述前端的功能,以及为什么最好不要重新实现它。

                    以下是 GHC 中使用的 Core 文档的一些链接:

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2023-03-21
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-01-28
                      • 1970-01-01
                      • 2011-08-15
                      • 1970-01-01
                      相关资源
                      最近更新 更多