Enums in Clojure
Our enum-like type ServerState has an underlying int type. In Clojure, we’ll use the keyword type to represent the different states a server can be in.
The possible values for ServerState are defined as constants:
(def ServerState
{:StateIdle :idle
:StateConnected :connected
:StateError :error
:StateRetrying :retrying})Implementing a Stringer-like function in Clojure can be done using a simple map lookup:
(def state-names
{(:StateIdle ServerState) "idle"
(:StateConnected ServerState) "connected"
(:StateError ServerState) "error"
(:StateRetrying ServerState) "retrying"})
(defn state-name [state]
(get state-names state))Next, our main function will emulate a state transition for a server; it takes the existing state and returns a new state:
(defn transition [state]
(case state
(:StateIdle ServerState) (:StateConnected ServerState)
(:StateConnected ServerState) (:StateIdle ServerState)
(:StateRetrying ServerState) (:StateIdle ServerState)
(:StateError ServerState) (:StateError ServerState)
(throw (Exception. (str "unknown state: " (state-name state))))))Finally, to see our state transitions in action, we can define a main function:
(defn -main []
(let [ns (transition (:StateIdle ServerState))]
(println (state-name ns))
(let [ns2 (transition ns)]
(println (state-name ns2)))))You can run the program using the following command:
$ clj -M -m your-namespaceHere’s what the output would look like:
connected
idleIn summary, this example demonstrates how to create and use enum-like constructs in Clojure by leveraging keywords and maps. Now that we understand how to create state transitions, let’s explore more features of the language.