DEV Community

Tito
Tito

Posted on • Edited on

A simple data oriented validation function

First Attempt

I'm going through Clojure for the Brave and True and one of the exercises was to write a function that validates a map against requirements.

I find this function quite beautiful because it declares the validations rules in a data oriented way.

; Define the requirements of each key in the record to be validated
; Name must be a key and glitter-index must be integer
(def requirements {:name string?
                   :glitter-index integer?})

; Validate a record (a map) against requirements
; Returns a list of validations results '(true false)
(defn validate
  [requirements record]
  ; map turns record {:a 1 :b 2} into a list of key-value pairs
  (map #((get requirements (key %)) (val %)) record))

; Usage:
(validate requirements {:name "Mr. Vampire" :glitter-index "42"})
; => (true false)
(validate requirements {:name "Mr. Vampire" :glitter-index 42})
; => (true true)
Enter fullscreen mode Exit fullscreen mode

The function is essentially a one liner and may be hard to grasp on first pass but once understood it starts to show some of the beauty of Clojure.

Second Attempt

I revisited this exercise after a couple of days and realized there was a simple but important bug.

Let say you wanted to update the requirements to check for age. You would update the requirement map but the validation function will ignore that new requirement.

(def requirements {:name string?
                   :glitter-index integer?
                   ; New requirement
                   :age integer?})

(validate requirements {:name "Edward Cullen", :glitter-index 10})
; Validate ignore the age requirement
; => (true, true)
Enter fullscreen mode Exit fullscreen mode

The cause is that the validate function uses the record map to determine which keys to check. The fix is simple and again begins to show the power of Clojure and s-expressions.

(defn old-validate
  [requirements record]
  ; Note how record was the input to map
  (map #((get requirements (key %)) (val %)) record))

(defn validate
  [requirements record]
  ; Note how requirements is now the input to map
  (map #((val %) (get record (key %))) requirements))

(validate requirements {:name "Edward Cullen", :glitter-index 10})
; :age is missing from the record and validate now returns a false
; => (true, true, false)
Enter fullscreen mode Exit fullscreen mode

Image of Datadog

The Future of AI, LLMs, and Observability on Google Cloud

Datadog sat down with Google’s Director of AI to discuss the current and future states of AI, ML, and LLMs on Google Cloud. Discover 7 key insights for technical leaders, covering everything from upskilling teams to observability best practices

Learn More

Top comments (0)

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay