Hello,
Reading about error handling I was thinking about my use of try/catch and about proper use of :pre
and :post
.
I don’t want to use any special libraries for error handling and I would like to stick with Clojure basics. But in my case I have two options.
I’m chaining a couple of tiny functions in a main function. I don’t want to have multiple try/catch in the main function for every tine function that needs it. So I want to throw
in those tiny functions.
Example of tiny function without error handling:
(def price-levels
{:expensive 300
:moderate 200
:cheap 100})
(defn get-price-level [level-name]
(level-name price-levels))
(get-price-level :moderate)
;; => 200
(get-price-level :wrong-name)
;; => nil
Basically it’s just an abstraction around :key map
.
But the main function HAS to get a number from the tiny function since I’m not going to consider / handle / unit-test possibility of nil
or anything else except number.
So I can write the tiny function as:
(defn get-price-level-2 [level-name]
(if-let [price (level-name price-levels)]
price
(throw (ex-info "Unable to get price." {:level-name level-name}))))
(get-price-level-2 :moderate)
;; => 200
(get-price-level-2 :wrong-name)
; Execution error (ExceptionInfo) at user/get-price-level-2 (REPL:15).
; Unable to get price.
or I can leverage :post
:
(defn get-price-level-3 [level-name]
{:post [(number? %)]}
(level-name price-levels))
(get-price-level-3 :moderate)
;; => 200
(get-price-level-3 :wrong-name)
;; Execution error (AssertionError) at user/get-price-level-3 (REPL:34).
;; Assert failed: (number? %)
or maybe also :pre
but that is probably overkill.
(defn get-price-level-4 [level-name]
{:pre [(keyword? level-name)]
:post [(number? %)]}
(level-name price-levels))
(get-price-level-4 :moderate)
;; => 200
(get-price-level-4 :wrong-name)
;; Execution error (AssertionError) at user/get-price-level-4 (REPL:33).
;; Assert failed: (number? %)
(get-price-level-4 "wrong input")
;; Execution error (AssertionError) at user/get-price-level-4 (REPL:33).
;; Assert failed: (keyword? level-name)
I like the :pre
and :post
syntax but one thing is bugging me - Clojure is dynamic. And I basically turned it into statically typed function.
Could you please tell me what is the Clojure-way? And what are Clojure-way approved use cases for :pre
and :post
?
Thank you, Ralf