Это проблема GHCi.
Та же ошибка возникает, когда ваш код копируется непосредственно в GHCi, что также дает ошибку разбора, когда он встречает новую строку после знака равенства.Эта первая ошибка не отображается здесь, потому что org-babel показывает только значение последнего выражения (в этом случае ошибка, вызванная пониманием списка).
Я не совсем знаком с тем, как режим Haskell отправляет код в GHCi, но похоже, что он включает загрузку в буфер в GHCi в виде файла, что может быть причиной того, что у вас не было этой проблемыработа с файлом hs
.
Существует несколько вариантов исправления, ни один из которых не является абсолютно идеальным:
- Переместить некоторую часть списка в первую строку (Например, первая строка может быть
pyth2 n = [
). - Обернуть все определение функции с помощью
:{
и :}
. - Написать функцию Elisp, чтобы изменить то, что отправляется в GHCi, изатем изменяет его после оценки.
Первые два варианта требуют, чтобы вы отформатировали код в форме, которую GHCi примет.В вашем примере, первый вариант может быть не слишком плохим, но это не всегда будет так тривиально для всех многострочных объявлений (например, объявлений функций сопоставления с образцом).Недостатком второго варианта является то, что он требует добавления скобок к коду, которого не должно быть в реальном исходном коде.
Чтобы решить проблему добавления посторонних скобок, я написал команду Elisp (my-org-babel-execute-haskell-blocks
), который помещает эти скобки вокруг блоков кода, которые он находит, оценивает область и затем удаляет скобки.Обратите внимание, что эта функция требует, чтобы блоки были отделены от всего остального кода хотя бы одной пустой строкой.
Вызов my-org-babel-execute-haskell-blocks
в вашем примере объявляет функцию без ошибок.
EDIT: Предыдущая функция, которую я дал, не работала с объявлениями сопоставления с образцом.Я переписал функцию, чтобы исправить эту проблему, а также чтобы быть в курсе комментариев.Эта новая функция должна быть значительно более полезной.Однако стоит отметить, что я не обрабатывал многострочные комментарии сложным образом, поэтому блоки кода с многострочными комментариями могут быть неправильно упакованы.
(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))))))