I use Emacs for reading and sending email, so I’ve been using emacs-lisp to send mailshots for years (but in a rather clunky way).

The big shortcoming is that it is not hugely convenient getting the data (e.g., student names, email addresses, marks, comments) into emacs-lisp data structures, or conveniently writing the emails.

org-mode makes it all easier.

I present an example here: how to send mails giving feedback on performance in a test.

First we put the data in an org-mode table, giving it a name. org-mode tables are very ergonomic to enter data directly in. You could also cut and paste a CSV and convert it (insert, highlight, type “C-|”), but it’s likely better to use org-mode to enter the data in the first place.

#+NAME: assignment1 | 1 | Sleepy | 5 | | 2 | Sneezy | 15 | | 3 | Doc | 10 |

Then we write the email (exactly the content of a message-mode Emacs mail-buffer, before sending), but with “%s” marking locations where we insert variable information. This is input as an org-mode EXAMPLE, with a name.

#+NAME: boilerplate #+BEGIN_EXAMPLE To: %s Subject: Assignment 1 BCC: brendan.halpin@ul.ie --text follows this line-- Dear %s, Homework assignment 1 Your mark on assignment 1 was %s/20. In other words, you %s. Regards, Snow White -- #+END_EXAMPLE

The boilerplate defines fields to fill as:

full email address

name

mark, and

a verbal summary of the performance.

Note that the boilerplate defines the complete contents of a mail message buffer (apart from the signature, which can be inserted afterwards, as below).

The next block explicitly refers to the student data and the boilerplate (“ :var boilerplate=boilerplate students=assignment1 “), and then defines a function, make-message , which works on each row of the student data table and

creates a mail buffer, deleting its contents in order to replace them

with our own

with our own inserts the boilerplate, populating the fields appropriately

including creating the verbal summary based on the mark

renames the buffer uniquely (and marks it unmodified, to make it easy

to delete if necessary)

The block then applies the defun to each element of the student-data list (using mapcar , which is like the apply family in R).

#+BEGIN_SRC emacs-lisp :results none :var boilerplate=boilerplate students=assignment1 ( defun make-message (student) (compose-mail) (delete-region (point-min) (point-max)) (insert (format boilerplate (format "dwarf%s@forest.mine" (nth 0 student)) (nth 1 student) (nth 2 student) ( cond ((< (nth 2 student) 10) "fluffed it" ) ((= (nth 2 student) 10) "did okay" ) ((> (nth 2 student) 10) "aced it" )))) (insert "Snow White Housekeeping and Judgementals The Dwarves' House Enchanted Forest" ) (rename-uniquely) (set-buffer-modified-p nil)) ;; make easy to delete for debug (mapcar ( lambda (student) (make-message student)) students) #+END_SRC

The end result is multiple mail buffers, one per student, each containing a mail message ready to send. You can then visit the buffers, and verify before sending. Insert the command message-send-and-exit at the end of the defun if you are truly reckless!

An example mail buffer:

To: dwarf3@forest.mine Subject: Assignment 1 BCC: brendan.halpin@ul.ie --text follows this line-- Dear Doc, Homework assignment 1 Your mark on assignment 1 was 10/20. In other words, you did okay. Regards, Snow White -- Snow White Housekeeping and Judgementals The Dwarves' House Enchanted Forest

This is nice because we can enter the data conveniently in an org-mode table, and similarly enter the email boilerplate in an org-mode EXAMPLE block without worrying too much about escaping quotation marks etc. However, what is really nice is that we can

do arbitrary things to the text using emacs-lisp (“you fluffed it”) process the data tables using, e.g,. R

It is also possible to generate arbitrarily complex HTML and multipart emails, but that’s a topic for another day.