;;; -*- lexical-binding: t -*-
;;;
;;; This is one of my config's weirder bits.  My config is organised into many
;;; disjoint "modules," configuring one group of related things at a time.  I
;;; want these modules to be as independent as possible — you should be able to
;;; add and remove individual modules and the only modules that should stop
;;; working are those which inherently build off of the removed module's
;;; functionality.  Thus, I want to discourage any reliance on some implicit
;;; load order — all dependencies should be explicit.  As one method to shake
;;; the tree of dependencies, so to speak, I came up with the idea of loading my
;;; "top-level" modules in a pseudo-random order, which has proven incredibly
;;; effective in catching accidental implicit dependencies thus far!

(require 'cl-lib)

(defconst syd-init-load-order-seed
  (or (when-let ((x (getenv "SYD_INIT_LOAD_ORDER_SEED")))
        (string-to-number x))
      (random))
  "Seed used to load top-level modules in pseudo-random order.  Loading modules
in an unpredictable order by default is a tactic to prevent implicit
dependencies between modules.  For debugging purposes, the load-order may be
made deterministic by starting Emacs with the environment variable
SYD_INIT_LOAD_ORDER_SEED set to a non-negative integer.")

;; Log as early as possible to make it difficult to miss.
(message "Top-level modules will be loaded in the order determined by SYD_INIT_LOAD_ORDER_SEED=%d"
         syd-init-load-order-seed)

(defun syd-random-permutation (lst &optional seed)
  "Return a random permutation of list LST using SEED as the random state.  The
permutation is deterministic for a given SEED."
  (let* ((random-state (cl-make-random-state (or seed (random))))
         (len (length lst))
         (vec (vconcat lst)))
    ;; Fisher-Yates shuffle.
    (cl-loop for i from (1- len) downto 1 do
             (cl-rotatef (aref vec i)
                         (aref vec (cl-random (1+ i) random-state))))
    ;; Convert vector to list.
    (append vec nil)))

(defun syd-require-features (features)
  "Require each of FEATURES in the pseudo-random order defined by
`syd-init-load-order-seed' and `syd-random-permutation'."
  (dolist (feature (syd-random-permutation
                    features syd-init-load-order-seed))
    (require feature)))

(provide 'syd/random-require)
