Don't want to get into a debate about whether something is a good idea or not, but that "beast" seems to go against clojures "data-all-the-things". In their example, you'd move a side effect into an otherwise "pure" function of taking text and turning it into data.
Maybe a little different solution, that uses data instead of throw
(defrecord LogEntry [valid? data error])
(defn parse-log-entry [text]
(if (looks-good? text)
(->LogEntry true (actually-parse text) nil)
(->LogEntry false nil (turn-into-error-message text))))
(defn explode-on-invalid-entries! [log]
(let [errors (->> log
(remove :valid?)
(map :error)
(doall))]
(when (seq errors)
(throw (ex-info "parse failed with invalid entries" {:errors errors})))
log))
(defn parse-log [log]
(->> (line-seq log)
(map parse-log-entry)
;; remove invalid entries
(filter :valid?)
(map :data)
;; or (replace invalid values with default)
(map (fn [{:keys [valid? data] :as it}]
(if valid? data default-data)))
;; or bail
(explode-on-invalid-entries!)
))
Just let parse-log-entry return whether the entry was ok or not. So we can handle it outside. Might just return nil on invalid entries, but that loses information. Avoid Exceptions whereever possible.
But again, I know nothing about CL so it might be a good idea to use restarts. IMHO its not a good idea in clojure, especially coupled with lazy-seqs.
But we are getting off-topic ...
Regards,
/thomas