【问题标题】:Haskell debugging an arbitrary lambda expressionHaskell 调试任意 lambda 表达式
【发布时间】:2015-09-07 02:22:49
【问题描述】:

我有一组要传递给其他 lambda 的 lambda 表达式。所有 lambda 只依赖于它们的参数,它们不调用任何外部函数。当然,有时它会变得很混乱,我会将参数数量不正确的函数传递给另一个函数,从而创建 GHCi 异常。

我想创建一个调试函数,它将采用任意 lambda 表达式(带有未知数量的参数)并根据 lambda 的结构和函数返回一个字符串。

例如,假设我有以下 lambda 表达式:

i = \x -> x
k = \x y -> x
s = \x y z -> x z (y z)

debug (s k) 应该返回 "\a b -> b"

debug (s s k) 应该返回 "\a b -> a b a"(如果我正确地简化了)

debug s 应该返回 "\a b c -> a c (b c)"

这样做的好方法是什么?

【问题讨论】:

  • 并不是在 Haskell 中,这类信息在编译时会被删除。您可能可以使用模板 haskell 来实现它,但这并不简单。
  • 那里有大量资源可用于实现 lambda 演算。 TaPL 是一个特别好的例子,包括不同复杂程度的微积分的实现。似乎还有一些关于 Hackage 的小型实现可供您使用。
  • Haskell 是一种静态类型语言。它实际上并没有那么深入地涉及 lambda 演算,除非 lambdas 恰好是编写笛卡尔封闭范畴表达式的有用方法。但是直接使用类型来了解函数的作用要比查看一些等效的 lambda 表达式要高效得多。对于您想要实现的目标,像 Scheme 这样的语言会更合适。
  • this 有帮助吗?

标签: debugging haskell lambda


【解决方案1】:

我认为这样做的方法是在 Haskell 中定义一个小的 lambda 演算 DSL(或使用现有的实现)。这样,您可以编写类似

的东西,而不是使用本机 Haskell 公式
k = Lam "x" (Lam "y" (App (Var "x") (Var "y")))
s = Lam "x" (Lam "y" (Lam "z" (App (App (Var "x") (Var "z")
                                   (App (Var "y") (Var "z"))))

si 也是如此。然后,您将编写/使用评估函数,以便您可以编写

debug e = eval e
debug (App s k)

这将为您提供您自己的语法的最终形式。此外,您需要一种解释器来将您的 DSL 语法转换为 Haskell,以便您可以实际使用代码中的函数。

实现这一点看起来确实有很多(棘手的)工作,而且它可能并不完全符合您的想法(特别是如果您需要对类型化语法进行评估),但我相信这将是一个很好的学习经验。一个很好的参考是chapter 6 of "Write you a Haskell"。使用现有的实现会容易得多(但没那么有趣:))。

如果这只是出于调试目的,您可能会受益于查看 ghc 编译到的核心语法。请参阅chapter 25 of Real world Haskell,要使用的 ghc 标志是 -ddump-simpl。但这意味着查看生成的代码,而不是在程序中生成表示。我也不确定你能在多大程度上轻松识别核心代码中的特定函数(我没有这方面的经验,所以 YMMV)。

如果在函数上使用 show 会给出你描述的那种输出,那当然会很酷,但函数不是 Show 的实例可能有很好的理由(我无法告诉你)。

【讨论】:

    【解决方案2】:

    您实际上可以通过使用 Template Haskell 中的漂亮打印来实现这一点,它是 GHC 开箱即用的。

    首先,格式化函数应该在单独的模块中定义(这是一个 TH 限制):

    module LambdaPrint where
    
    import Control.Monad
    import Language.Haskell.TH.Ppr
    import Language.Haskell.TH.Syntax
    
    showDef :: Name -> Q Exp
    showDef = liftM (LitE . StringL . pprint) . reify
    

    然后使用它:

    {-# LANGUAGE TemplateHaskell #-}
    import LambdaPrint
    
    y :: a -> a
    y = \a -> a
    $(return []) --workaround for GHC 7.8+
    
    test = $(showDef 'y)
    

    结果或多或少具有可读性,不包括完全限定名称:

    *Main> test
    "Main.y :: forall a_0 . a_0 -> a_0"
    

    关于发生了什么的几句话。 showDef 是一个宏函数,它从环境中重新定义某个名称的定义,并将其漂亮地打印在字符串文字表达式中。要使用它,您需要引用 lambda 的名称(使用 ')并将结果(这是一个带引号的字符串表达式)拼接成某个表达式(使用 $(...))。

    【讨论】:

    • 很抱歉,我的解决方案似乎是错误的。 reifying 变量声明实际上并不包含它的定义,所以 showDef 只显示类型。这就是文档对主体的描述:“目前,这个值总是什么都没有:由于缺乏兴趣,还没有实现返回 RHS。”
    猜你喜欢
    • 2014-04-08
    • 2013-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多