[英]Haskell with emacs org-mode: Variable not in scope
在从以前沮丧地徘徊之后,我决定再次尝试在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
和单个代码块中无效。 此代码在单独的REPL中运行的单独hs
文件中正常工作。 单独文件中的pyth2
代码也可以从org-mode启动的REPL中调用,也可以正常运行。 不知道如何继续。 如有必要,可以包含Emacs init信息。
这是一个GHCi问题。
当您的代码直接复制到GHCi时会发生同样的错误,这也会在等号后遇到新行时产生解析错误。 这个第一个错误没有显示在这里,因为org-babel只显示最后一个表达式的值(在这种情况下,是由列表推导引起的错误)。
我并不完全熟悉Haskell模式如何将代码发送到GHCi,但看起来它涉及将缓冲区加载到GHCi中作为文件,这可能就是为什么你没有从hs
文件中解决这个问题。
有几个选项可以解决这个问题,其中没有一个是完全理想的:
pyth2 n = [
)。 :{
和:}
包装整个函数定义。 前两个选项要求您以GHCi将接受的形式格式化您的代码。 在您的示例中,第一个选项可能不会太糟糕,但对于所有多行声明(例如模式匹配函数声明),这并不总是那么简单。 第二个选项的缺点是它需要在代码中添加括号,这些代码不应该存在于实际源代码中。
为了解决添加无关括号的问题,我编写了一个Elisp命令( my-org-babel-execute-haskell-blocks
),将这些括号放在它找到的代码块周围,评估区域,然后删除括号。 请注意,此函数要求使用至少一个空行将块与所有其他代码分开。
在您的示例上调用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))))))
在组织模式邮件列表上,我得到了一个答案,基本上和你说的一样,D。Gillis。 他有一个类似的解决方案,实际上更多以组织模式为中心。 在你的代码块放在这个“抽屉”的标题下
:PROPERTIES:
:header-args:haskell: :prologue ":{\n" :epilogue ":}\n"
:END:
然后(可能在局部变量中)运行
#+begin_src haskell :results output
:set prompt-cont ""
#+end_src
由于未知的原因,我必须包括:results output
否则会出现“期待字符串”的神秘错误。
在其他几个注释中,haskell babel不响应/关心:session
选项,即当你运行代码块时,REPL *haskell*
启动,这将是唯一的REPL。 此外, haskell-mode
启动REPL与现有组织haskell-mode
启动的REPL不兼容,即,如果从haskell-mode
启动REPL,它会杀死原始组织模式*haskkell*
REPL,以及任何新尝试运行org-mode代码块无法看到这个新的,非*haskell*
REPL。 然后,如果你杀死haskell-mode
REPL并尝试运行org-mode块,你就得到了
executing Haskell code block...
inferior-haskell-start-process: List contains a loop: ("--no-build" "--no-load" "--ghci-options=-ferror-spans" "--no-build" "--no-load" . #2)
...你被软管 - 似乎没有动摇它,没有任何重启/刷新,也没有杀死,重新加载文件,即完全重启Emacs是必要的。 任何人都知道更好的解决方案,请告诉我们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.