# Where parallels cross

Interesting bits of life

# The Poor Org-User Spaced Repetition

Use vanilla Org Mode for your space repetitions. I show you how a bit of Elisp (jump to the bottom to try out) can transform your agenda files into an effective learning exercise. You can integrate this with anything, even org-roam!

## The problem

Before discovering the (mandatory) Learning how to learn course, I got in touch with the concept of spaced learning. In short a trick to learn effectively: you fix memories by repeating learning sessions over spaced period of times. Now I was already an Emacs user and I was sure Org Mode should have become my knowledge vector. The only thing: external packages required me to encode my knowledge in a certain way to present it to me at given intervals.

## It is a problem indeed

I dislike extra effort and always try to make the most of my laziness (attempting to emulate Haskell). And the fact that I had all this headings stored with precious beacons of knowledge pained me: how many doors was that missing knowledge keeping shut for me?

That thought anguished me enough to push me to write some code.

## And there is a solution

The idea was: I want this knowledge to pop up in my Org Agenda, then I want to review that knowledge, then I want this rescheduled automatically for later and disappear from my agenda.

This design guided me to the following code:

(defun my/space-repeat-if-tag-spaced (e)
"Resets the header on the TODO states and increases the date
according to a suggested spaced repetition interval."
(let* ((spaced-rep-map '((0 . "++1d")
(1 . "++2d")
(2 . "++10d")
(3 . "++30d")
(4 . "++60d")
(5 . "++4m")))
(spaced-key "spaced")
(tags (org-get-tags nil t))
(spaced-todo-p (member spaced-key tags))
(repetition-n (first (cdr spaced-todo-p)))
(n+1 (if repetition-n (+ 1 (string-to-number (substring repetition-n (- (length repetition-n) 1) (length repetition-n)))) 0))
(spaced-repetition-p (alist-get n+1 spaced-rep-map))
(new-repetition-tag (concat "repetition" (number-to-string n+1)))
(new-tags (reverse (if repetition-n
(seq-reduce
(lambda (a x) (if (string-equal x repetition-n) (cons new-repetition-tag a) (cons x a)))
tags
'())
(seq-reduce
(lambda (a x) (if (string-equal x spaced-key) (cons new-repetition-tag (cons x a)) (cons x a)))
tags
'())))))
(if (and spaced-todo-p spaced-repetition-p)
(progn
;; avoid infinitive looping
(remove-hook 'org-trigger-hook 'my/space-repeat-if-tag-spaced)
;; reset to previous state
(org-call-with-arg 'org-todo 'left)
;; schedule to next spaced repetition
(org-schedule nil (alist-get n+1 spaced-rep-map))
;; rewrite local tags
(org-set-tags new-tags)
)))



* Some important knowledge :spaced:
SCHEDULED <someTime>


will appear in my agenda according to the time interval in the spaced-rep-map let over lambda (which is also the title of a lovely Lispy book, by the way). If I tick it done, the program will reschedule it and add a new tag to it :repetition1:, so I can keep track of my progress.

Note that I set the map to the intervals that I found in some reference at the time:

((0 . "++1d")
(1 . "++2d")
(2 . "++10d")
(3 . "++30d")
(4 . "++60d")
(5 . "++4m"))


After 5 repetitions and some months I expect to have learned my knowledge. You can easily set your own period and more (or less) repetitions.

I have used this method for years now and I must say it works for me. At the beginning of the year I coupled this with org-roam notes, and I am loving it.

In particular something I like to do with org-roam is to have special Elisp links in my headings, they look like:

* TODO [[elisp:(org-roam-graph 1 "/org-roam-notes-path/someNote.org")][review someNote]]             :spaced:repetition4:
SCHEDULED: <2020-10-30 Fri>


This way I can review my knowledge through the power of graphviz's dot diagrams looking like:

See org-roam documentation on how to make the nodes clickable!

## Conclusion

I believe this is a precious snippet of my configuration, and I hope you will enjoy as much as I have.

So just run that snippet and create a knowledge task: give a try to space repetition, you may find out how to learn better and more easily!

I am always curious about the reader smartness: how are you going to use this? Just get in touch if you wish to share it and exchange ideas!

## Update 2020-09-23

Thanks to Art, I discovered that the above code does not work in a more recent version of Emacs. This is the fixed code that uses the new API:

(defun my/space-repeat-if-tag-spaced (e)
"Resets the header on the TODO states and increases the date
according to a suggested spaced repetition interval."
(let* ((spaced-rep-map '((0 . "++1d")
(1 . "++2d")
(2 . "++10d")
(3 . "++30d")
(4 . "++60d")
(5 . "++4m")))
(spaced-key "spaced")
(tags (org-get-tags))
(spaced-todo-p (member spaced-key tags))
(repetition-n (car (cdr spaced-todo-p)))
(n+1 (if repetition-n (+ 1 (string-to-number (substring repetition-n (- (length repetition-n) 1) (length repetition-n)))) 0))
(spaced-repetition-p (alist-get n+1 spaced-rep-map))
(new-repetition-tag (concat "repetition" (number-to-string n+1)))
(new-tags (reverse (if repetition-n
(seq-reduce
(lambda (a x) (if (string-equal x repetition-n) (cons new-repetition-tag a) (cons x a)))
tags
'())
(seq-reduce
(lambda (a x) (if (string-equal x spaced-key) (cons new-repetition-tag (cons x a)) (cons x a)))
tags
'())))))
(if (and spaced-todo-p spaced-repetition-p)
(progn
;; avoid infinitive looping
(remove-hook 'org-trigger-hook 'my/space-repeat-if-tag-spaced)
;; reset to previous state
(org-call-with-arg 'org-todo 'left)
;; schedule to next spaced repetition
(org-schedule nil (alist-get n+1 spaced-rep-map))
;; rewrite local tags
(org-set-tags-to new-tags)