Subject: bug#40852: Support compilation of Haskell in org mode babel blocks.

From: Roland Coeurjoly <[email-masked]>
Date: Sat, 25 Apr 2020 21:04:03 +0200

Haskell code can be both compiled (for example with ghc), or interpreted
(with ghci).

Until now, org babel had only support for interpretation.

Haskell is weird in that some code for the interpreter cannot be compiled
and viceversa.
For example, in ghci (the interpreter) you are required to use let to
declare functions
<https://stackoverflow.com/questions/28927716/org-babel-for-haskell-not-works-of-eval-haskell-block/40920873>
.

In this patch I add support for compilation with the header argument
:compile yes.
The function to compile haskell is almost a copy paste of the C funcion in
ob-C.el.

By default I retain the original behavior, i.e. interpreting the block.

I have tested this patch in emacs-27.0.91.

It is my first patch to GNU Emacs and I am a newbie with both elisp and
haskell.
From f7a7273dbf034445e0bf48c2eb2d2d16fed6668d Mon Sep 17 00:00:00 2001
From: Roland Coeurjoly <[email-masked]>
Date: Sat, 25 Apr 2020 20:35:22 +0200
Subject: [PATCH] Add Haskell specific header argument compile, to compile
 instead of interpret the body of source block

---
 lisp/org/ob-haskell.el | 75 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 70 insertions(+), 5 deletions(-)

diff --git a/lisp/org/ob-haskell.el b/lisp/org/ob-haskell.el
index e004a3405e..c945136f33 100644
--- a/lisp/org/ob-haskell.el
+++ b/lisp/org/ob-haskell.el
@@ -23,12 +23,13 @@
 
 ;;; Commentary:
 
-;; Org-Babel support for evaluating haskell source code.  This one will
-;; be sort of tricky because haskell programs must be compiled before
+;; Org-Babel support for evaluating Haskell source code.
+;; Haskell programs must be compiled before
 ;; they can be run, but haskell code can also be run through an
 ;; interactive interpreter.
 ;;
-;; For now lets only allow evaluation using the haskell interpreter.
+;; By default we evaluate using the Haskell interpreter.
+;; To use the compiler, specify :compile yes in the header.
 
 ;;; Requirements:
 
@@ -60,8 +61,64 @@ org-babel-haskell-eoe
 
 (defvar haskell-prompt-regexp)
 
-(defun org-babel-execute:haskell (body params)
-  "Execute a block of Haskell code."
+(defcustom org-babel-Haskell-compiler "ghc"
+  "Command used to compile a Haskell source code file into an executable.
+May be either a command in the path, like ghc
+or an absolute path name, like /usr/local/bin/ghc
+parameter may be used, like ghc -v"
+  :group 'org-babel
+  :version "27.0"
+  :type 'string)
+
+(defconst org-babel-header-args:haskell '((compile . :any))
+  "Haskell-specific header arguments.")
+
+(defun org-babel-Haskell-execute (body params)
+  "This function should only be called by `org-babel-execute:haskell'"
+  (let* ((tmp-src-file (org-babel-temp-file
+			"Haskell-src-"
+                        ".hs"))
+         (tmp-bin-file
+          (org-babel-process-file-name
+           (org-babel-temp-file "Haskell-bin-" org-babel-exeext)))
+         (cmdline (cdr (assq :cmdline params)))
+         (cmdline (if cmdline (concat " " cmdline) ""))
+         (flags (cdr (assq :flags params)))
+         (flags (mapconcat 'identity
+		           (if (listp flags) flags (list flags)) " "))
+         (libs (org-babel-read
+	        (or (cdr (assq :libs params))
+	            (org-entry-get nil "libs" t))
+	        nil))
+         (libs (mapconcat #'identity
+		          (if (listp libs) libs (list libs))
+		          " ")))
+    (with-temp-file tmp-src-file (insert body))
+    (org-babel-eval
+     (format "%s -o %s %s %s %s"
+             org-babel-Haskell-compiler
+	     tmp-bin-file
+	     flags
+	     (org-babel-process-file-name tmp-src-file)
+	     libs) "")
+    (let ((results
+	   (org-babel-eval
+	    (concat tmp-bin-file cmdline) "")))
+      (when results
+        (setq results (org-trim (org-remove-indentation results)))
+        (org-babel-reassemble-table
+         (org-babel-result-cond (cdr (assq :result-params params))
+	   (org-babel-read results t)
+	   (let ((tmp-file (org-babel-temp-file "Haskell-")))
+	     (with-temp-file tmp-file (insert results))
+	     (org-babel-import-elisp-from-file tmp-file)))
+         (org-babel-pick-name
+	  (cdr (assq :colname-names params)) (cdr (assq :colnames params)))
+         (org-babel-pick-name
+	  (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))
+      )))
+
+(defun org-babel-interpret-Haskell (body params)
   (require 'inf-haskell)
   (add-hook 'inferior-haskell-hook
             (lambda ()
@@ -96,6 +153,14 @@ org-babel-execute:haskell
      (org-babel-pick-name (cdr (assq :rowname-names params))
 			  (cdr (assq :rowname-names params))))))
 
+
+(defun org-babel-execute:haskell (body params)
+  "Execute a block of Haskell code."
+  (setq compile (string= (cdr (assq :compile params)) "yes"))
+  (if (not compile)
+      (org-babel-interpret-Haskell body params)
+    (org-babel-Haskell-execute body params)))
+
 (defun org-babel-haskell-initiate-session (&optional _session _params)
   "Initiate a haskell session.
 If there is not a current inferior-process-buffer in SESSION
-- 
2.20.1