slingshot Clojure Library

repository·release·Indexed 20 days ago

https://github.com/scgilardi/slingshot

A Clojure library providing enhanced try+ and throw+ macros. It allows throwing and catching arbitrary Clojure data, such as maps or records, as exceptions. Features include advanced catch selectors based on class names, key-value vectors, predicates, and selector forms, as well as support for Clojure destructuring in catch clauses and an optional else clause for successful completions.

Tokens
998
Snippets
4
Records
5
Agent score
21%

What's inside slingshot

  1. Use try+ with advanced catch selectors

    release

    The try+ macro provides enhanced catch clauses that can select exceptions based on more than just class types. A catch clause will execute for the first selector that matches the thrown object.

    Supported selector types:

    • Class name: (e.g., RuntimeException, my.clojure.record) matches any instance of that class.
    • Key-values vector: (e.g., [key val & kvs]) matches objects where (and (= (get object key) val) ...) is true.
    • Predicate: A function of one argument (e.g., map?, set?) that returns a truthy value.
    • Selector form: A form containing one or more % instances to be replaced by the thrown object (e.g., (some-logic %)).
    (try+
      (do-something)
      (catch [:type :tensor.parse/bad-tree] {:keys [tree hint]}
        ;; matches if (get object :type) is :tensor.parse/bad-tree
        (println "Caught bad tree:" tree))
      (catch Object _
        ;; matches any other object
        (println "Caught something else")))
  2. Destructure caught exceptions in try+

    release

    The binding in a try+ catch clause is not limited to a single symbol; it supports Clojure destructuring. This allows you to directly extract values from a thrown map or record within the catch block.

    (try+
      (throw+ {:type ::error :msg "failed"})
      (catch [:type :error] {:keys [msg]}
        (println "Error message was:" msg))) ; msg will be "failed"
  3. Use throw+ to throw non-Throwable objects

    release

    Unlike Clojure's native throw, throw+ can throw any Java object, such as Clojure maps or records. This allows you to represent custom exceptions without using gen-class. When a non-Throwable object is thrown, slingshot wraps it in an IExceptionInfo wrapper to maintain compatibility with Clojure/Java exception handling.

    (ns tensor.parse
      (:use [slingshot.slingshot :only [throw+]]))
    
    (defn parse-tree [tree hint]
      (if (bad-tree? tree)
        (throw+ {:type ::bad-tree :tree tree :hint hint})
        (parse-good-tree tree hint)))
  4. Access throw context via &throw-context

    release

    Inside a try+ catch clause, you can access metadata about the exception using the hidden argument &throw-context. This map provides details about the caught object and its origin.

    For Throwable caught objects:

    • :object: The caught object.
    • :message: The message from .getMessage.
    • :cause: The cause from .getCause.
    • :stack-trace: The stack trace from .getStackTrace.
    • :throwable: The caught object.

    For non-Throwable caught objects (including maps via ex-info):

    • :object: The caught object.
    • :message: The message from throw+ or ex-info.
    • :cause: The cause from throw+ or ex-info.
    • :stack-trace: The captured stack trace.
    • :wrapper: The Throwable wrapper that carried the object.
    • :throwable: The outermost Throwable whose cause chain contains the wrapper.
    (try+
      (throw+ {:info :data})
      (catch Object _
        (let [ctx &throw-context]
          (println "Caught object:" (:object ctx))
          (println "Stack trace:" (:stack-trace ctx))))