emacs - Haskell 与 emacs 组织模式 : Variable not in scope

标签 emacs org-mode haskell-stack

在与之前的挫折中徘徊之后,我决定再次在 Emacs org-mode 中尝试 Haskell。我正在使用 Haskell stack-ghci (8.6.3)、Emacs 26.2、org-mode 9.2.3 设置为 intero .这个代码块

#+begin_src haskell :results raw :session *haskell*
pyth2 :: Int -> [(Int, Int, Int)]
pyth2 n =
  [ (x, y, z)
  | x <- [1 .. n]
  , y <- [x .. n]
  , z <- [y .. n]
  , x ^ 2 + y ^ 2 == z ^ 2
  ]
#+end_src

产生这个结果:
*Main| *Main| *Main| *Main| *Main| 
<interactive>:59:16: error: Variable not in scope: n
<interactive>:60:16: error: Variable not in scope: n
<interactive>:61:16: error: Variable not in scope: n

然而,这
#+begin_src haskell :results raw
tripleMe x = x + x + x
#+end_src

工作正常。我添加了 :set +m两个ghci.conf并且单个代码块无效。此代码在单独的 hs 中运行良好文件在单独的 REPL 中运行。 pyth2单独文件中的代码也可以从 org-mode 启动的 REPL 中调用,并且也可以正常运行。不知道如何进行。如有必要,可以包含 Emacs 初始化信息。

最佳答案

这是一个 GHCi 问题。

当您的代码直接复制到 GHCi 时也会出现同样的错误,当遇到等号后的新行时也会出现解析错误。第一个错误没有出现在这里,因为 org-babel 只显示最后一个表达式的值(在这种情况下,由列表理解引起的错误)。

我并不完全熟悉 Haskell 模式如何将代码发送到 GHCi,但看起来它涉及将缓冲区作为文件加载到 GHCi 中,这可能是您从 hs 工作时没有遇到此问题的原因。文件。

有几个选项可以解决这个问题,但都不是完全理想的:

  • 将列表的某些部分移动到第一行(例如,第一行可以是 pyth2 n = [ )。
  • :{ 包裹整个函数定义和 :} .
  • 编写一个 Elisp 函数来修改发送到 GHCi 的内容,然后在评估后将其更改回来。

  • 前两个选项要求您以 GHCi 接受的格式格式化您的代码。在您的示例情况下,第一个选项可能还不错,但是对于所有多行声明(例如模式匹配函数声明)来说,这并不总是那么简单。第二个选项的缺点是它需要在实际源代码中不应该存在的代码中添加括号。

    为了解决添加多余括号的问题,我编写了一个 Elisp 命令 (my-org-babel-execute-haskell-blocks),它将这些括号放在它找到的代码块周围,评估区域,然后删除括号。请注意,此功能要求将 block 与所有其他代码分开,至少有一个空行。

    调用my-org-babel-execute-haskell-blocks在您的示例中声明该函数没有任何错误。

    编辑:我之前给出的函数无法处理模式匹配声明。我已经重写了函数来解决这个问题并注意评论。这个新功能应该会更有用。但是,值得注意的是,我没有以复杂的方式处理多行注释,因此具有多行注释的代码块可能无法正确包装。
    (defun my-org-babel-execute-haskell-blocks ()
      "Wraps :{ and :} around all multi-line blocks and then evaluates the source block.
    Multi-line blocks are those where all non-indented, non-comment lines are declarations using the same token."
      (interactive)
      (save-excursion
        ;; jump to top of source block
        (my-org-jump-to-top-of-block)
        (forward-line)
        ;; get valid blocks
        (let ((valid-block-start-ends (seq-filter #'my-haskell-block-valid-p (my-get-babel-blocks))))
          (mapcar #'my-insert-haskell-braces valid-block-start-ends)
          (org-babel-execute-src-block)
          (mapcar #'my-delete-inserted-haskell-braces (reverse valid-block-start-ends)))))
    
    
    (defun my-get-blocks-until (until-string)
      (let ((block-start nil)
            (block-list nil))
        (while (not (looking-at until-string))
          (if (looking-at "[[:space:]]*\n")
              (when (not (null block-start))
                (setq block-list (cons (cons block-start (- (point) 1))
                                       block-list)
                      block-start nil))
            (when (null block-start)
              (setq block-start (point))))
          (forward-line))
        (when (not (null block-start))
          (setq block-list (cons (cons block-start (- (point) 1))
                                 block-list)))))
    
    (defun my-get-babel-blocks ()
      (my-get-blocks-until "#\\+end_src"))
    
    (defun my-org-jump-to-top-of-block ()
      (forward-line)
      (org-previous-block 1))
    
    (defun my-empty-line-p ()
      (beginning-of-line)
      (= (char-after) 10))
    
    (defun my-haskell-type-declaration-line-p ()
      (beginning-of-line)
      (and (not (looking-at "--"))
           (looking-at "^.*::.*$")))
    
    (defun my-insert-haskell-braces (block-start-end)
      (let ((block-start (car block-start-end))
            (block-end (cdr block-start-end)))
        (goto-char block-end)
        (insert "\n:}")
        (goto-char block-start)
        (insert ":{\n")))
    
    
    (defun my-delete-inserted-haskell-braces (block-start-end)
      (let ((block-start (car block-start-end))
            (block-end (cdr block-start-end)))
        (goto-char block-start)
        (delete-char 3)
        (goto-char block-end)
        (delete-char 3)))
    
    
    (defun my-get-first-haskell-token ()
      "Gets all consecutive non-whitespace text until first whitespace"
      (save-excursion
        (beginning-of-line)
        (let ((starting-point (point)))
          (re-search-forward ".*?[[:blank:]\n]")
          (goto-char (- (point) 1))
          (buffer-substring-no-properties starting-point (point)))))
    
    
    (defun my-haskell-declaration-line-p ()
      (beginning-of-line)
      (or (looking-at "^.*=.*$")  ;; has equals sign
          (looking-at "^.*\n[[:blank:]]*|")
          (looking-at "^.*where[[:blank:]]*$")))
    
    
    (defun my-haskell-block-valid-p (block-start-end)
      (let ((block-start (car block-start-end))
            (block-end (cdr block-start-end))
            (line-count 0))
            (save-excursion
              (goto-char block-start)
              (let ((token 'nil)
                    (is-valid t))
                ;; eat top comments
                (while (or (looking-at "--")
                           (looking-at "{-"))
                  (forward-line))
                (when (my-haskell-type-declaration-line-p)
                  (progn
                    (setq token (my-get-first-haskell-token)
                          line-count 1)
                    (forward-line)))
                (while (<= (point) block-end)
                  (let ((current-token (my-get-first-haskell-token)))
                    (cond ((string= current-token "") ; line with indentation
                           (when (null token) (setq is-valid nil))
                           (setq line-count (+ 1 line-count)))
                          ((or (string= (substring current-token 0 2) "--") ;; skip comments
                               (string= (substring current-token 0 2) "{-"))
                           '())
                          ((and (my-haskell-declaration-line-p)
                                (or (null token) (string= token current-token)))
                           (setq token current-token
                                 line-count (+ 1 line-count)))
                          (t (setq is-valid nil)
                             (goto-char (+ 1 block-end))))
                    (forward-line)))
                (and is-valid (> line-count 1))))))
    

    关于emacs - Haskell 与 emacs 组织模式 : Variable not in scope,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56332560/

    相关文章:

    emacs - Racket REPL 和子模块

    emacs - lex/flex/yacc/bison文件是否有良好的Emacs模式或方法?

    macos - 如何在 OSX 上的 GNU Emacs 中获得透明窗口?

    haskell - 如何使用堆栈处理单个文件?

    haskell - 是否可以让 Haskell 堆栈使用 nix 提供的 Haskell 包?

    python - Emacs:检查文件路径是否包含目录名称

    emacs - 除了编辑 emacs 之外,emacs LISP 的可行性如何?

    emacs - 如何在组织模式下设置 ODT 导出的页边距?

    css - 在 CSS 中创建带有标题/图例的框

    haskell - 了解Haskell的堆栈程序以及解析器和LTS版本