Skip to content

biutthapa/vary

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Vary

https://img.shields.io/clojars/v/com.biutthapa/vary.svg

Vary is a Clojure library for defining sum-type data structures with a concise hashmap syntax, Malli validation, and pattern matching.

Features

  • vary macro for succinct variant definitions
  • Simple keywords for cases
  • cases function for associated values inspection.
  • match macro for pattern matching.
  • Auto-generated accessors
  • REPL-friendly for interactive workflows.

Installation

For Clojure CLI/deps.edn:

com.biutthapa/vary {:mvn/version "0.0.1"}

For Leiningen/Boot:

[com.biutthapa/vary "0.0.1"]

Usage

Define variants:

(ns coffee-shop
  (:require [vary.core :refer [vary cases match]]))

;; Simple variant with keywords
(vary Color #{:red :blue :green})

;; Variant with associated values
(vary CoffeeSize
  {:small  {:label "Small"  :price 3.50 :volume 8}
   :medium {:label "Medium" :price 4.00 :volume 12}
   :large  {:label "Large"  :price 4.50 :volume 16}})

;; Variant with fields
(vary OrderStatus
  {:pending []
   :completed [:amount :double]
   :failed [:reason :string]})

Inspect cases:

(cases Color)
;; => {:red {}, :blue {}, :green {}}

(cases CoffeeSize)
;; => {:small {:label "Small" :price 3.50 :volume 8}, ...}

Iterate through associated values (for variants with associated values):

(doseq [[size props] (cases CoffeeSize)]
  (println (:label props) ": $" (format "%.2f" (:price props)) " for " (:volume props) " oz"))
;; => Small: $3.50 for 8 oz
;;    Medium: $4.00 for 12 oz
;;    Large: $4.50 for 16 oz

Use match for pattern matching:

;; For simple cases
(defn color-name [color]
  (match color Color
    :red "Red"
    :blue "Blue"
    :green "Green"))

(println (color-name :blue)) ;; => "Blue"

;; For fields
(defn handle-order [order]
  (match order OrderStatus
    :pending "Order is pending"
    [:completed amount] (str "Completed with amount: " amount)
    [:failed reason] (str "Failed: " reason)))

(println (handle-order {:type :completed :amount 5.50})) ;; => "Completed with amount: 5.50"

Access associated values with auto-generated accessors (only for variants with associated values):

(defn order-summary [order]
  (let [size (:size order)]
    (str "Order: " (:coffee-type order) " (" (CoffeeSize-label size)
         ", " (CoffeeSize-volume size) " oz) - $" (format "%.2f" (CoffeeSize-price size)))))

(println (order-summary {:coffee-type "Latte" :size :medium}))
;; => Order: Latte (Medium, 12 oz) - $4.00

Note: No accessors are generated for simple variants like Color.

Linting

Add the following to your .clj-kondo/config.edn:

{:lint-as {vary.core/vary clojure.core/def}
 :hooks
 {:analyze-call
  {vary.core/vary
   {:analyze
    (fn [{:keys [:node]}]
      (let [children (:children node)
            vary-name (when (>= (count children) 2) (nth children 1))
            case-map (when (>= (count children) 3) (nth children 2))
            is-symbol-node (fn [n] (and (map? n) (= :token (:tag n))))
            is-map-node (fn [n] (and (map? n) (= :map (:tag n))))
            is-keyword-node (fn [n] (and (map? n) (= :keyword (:tag n))))
            associated-values (when (and vary-name case-map
                                         (is-symbol-node vary-name)
                                         (is-map-node case-map))
                                (->> (:children case-map)
                                     (partition 2)
                                     (map second)
                                     (filter is-map-node)
                                     (mapcat :children)
                                     (partition 2)
                                     (map first)
                                     (filter is-keyword-node)
                                     (map :value)
                                     (map name)
                                     distinct))]
        {:defined-by :clj-kondo.hooks/defined
         :defined-fns (if (and vary-name associated-values (is-symbol-node vary-name))
                        (mapv (fn [field]
                                (symbol (str (name (:value vary-name)) "-" field)))
                              associated-values)
                        [])}))}}}}

License

MIT License. See LICENSE file.

About

Elegant variant types for Clojure

Resources

License

Stars

Watchers

Forks

Packages

No packages published