【问题标题】:Haskell: Finding the source of incomplete record constructionHaskell:寻找不完整记录构造的根源
【发布时间】:2015-07-06 06:10:35
【问题描述】:

我正在尝试在 Haskell 中调试一个大型、复杂的程序,这并不是我自己编写的。

我正在尝试打印我的数据结构来诊断错误,但是当我这样做时,我收到以下错误:error: Prelude.undefined。如您所见,此错误信息极少。

我有理由确定这是来自我已“部分”初始化的记录,我正在尝试访问尚未设置值的字段。

程序(编译器)分布在两个 cabal 项目中,一个库和一个使用该库的可执行文件。这使得使用 GHCI/cabal-repl 进行调试变得困难:我无法在可执行文件上运行使用 GHCi,因为它不是错误的来源,但是重新创建可执行文件提供给库的输入太复杂而无法手动完成。

我想知道:我可以做些什么来获取有关创建不正确记录的位置、错误来源是哪个字段等的更多信息。是否有 RTS 选项或我可以用来提供更多信息的东西错误输出?

【问题讨论】:

    标签: debugging haskell ghc ghci


    【解决方案1】:

    如果您在 cabal 文件的 ghc-options 中使用 -Wall,它会给出记录不完整的警告。

    module Foo where
    data Bar = Bar { bar :: String, baz :: String }
    f = Bar { baz = "foo" }
    
    Foo.hs:5:5: Warning:
      Fields of ‘Bar’ not initialised: bar
      In the expression: Bar {baz = "foo"}
      In an equation for ‘f’: f = Bar {baz = "foo"}
    Ok, modules loaded: Foo.
    

    【讨论】:

      【解决方案2】:

      使用 -Wall(或 -Werror)编译是寻找未初始化字段来源的良好开端。但是,它可能不是错误的来源。缺少记录字段初始化有一个特定的错误消息:

      Prelude> data A = A { a :: Int } deriving Show
      Prelude> A {}
      A {a = *** Exception: <interactive>:11:1-4: Missing field in record construction a
      

      如果给记录字段添加严格注解,编译时会报错:

      Prelude> data A = A { a :: !Int } deriving Show
      Prelude> let a = A { }
      
      <interactive>:26:9:
          Constructor ‘A’ does not have the required strict field(s): a
          In the expression: A {}
          In an equation for ‘a’: a = A {}
      

      查找错误源的另一个选项是在启用分析的情况下进行编译并传递 -xc RTS 标志。

      来自 GHC 用户指南: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/runtime-control.html

      -xc (仅在编译程序进行分析时可用。)当程序中引发异常时,此选项会导致堆栈 要转储到 stderr 的跟踪。

      这对于调试特别有用:如果您的程序是 抱怨 head [] 错误而你不知道是哪一点 的代码导致它,使用 -prof -fprof-auto 编译并运行 使用 +RTS -xc -RTS 会准确地告诉您此时的调用堆栈 引发了错误。

      对于在 程序(程序可能会引发并捕获几个异常 它的执行),每个报告看起来像这样:

      *** 引发异常(由于 +RTS -xc 导致报告),堆栈跟踪:GHC.List.CAF --> 评估者:Main.polynomial.table_search,
      从 Main.polynomial.theta_index 调用,从调用 Main.polynomial,调用自 Main.zonal_pressure,调用自 Main.make_pressure.p,调用自 Main.make_pressure,调用自 Main.compute_initial_state.p,调用自 Main.compute_initial_state,从 Main.CAF 调用...

      【讨论】:

      • 我不知道您实际上可以获得堆栈跟踪。可惜你需要重新编译整个 Haskell 平台来进行分析,但是嘿......
      • 谢天谢地,我过去在分析方面遇到了足够多的麻烦,以至于我已经在启用分析的情况下编译了整个沙箱...
      猜你喜欢
      • 2012-01-31
      • 2023-03-25
      • 1970-01-01
      • 2016-11-24
      • 2010-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-13
      相关资源
      最近更新 更多