Liking cljdoc? Tell your friends :D

Lasertag

A library for categorizing values in Clojure, ClojureScript, and Babashka.

Lasertag took shape while developing of the colorizing pretty-printing engine used by Bling and Fireworks.


Examples

The function lasertag.core/tag will return a descriptive tag:

(require '[lasertag.core :refer [tag tag-map]])

(tag "hi")      ; => :string
(tag :hi)       ; => :keyword
(tag "#^hi$")   ; => :regex
(tag true)      ; => :boolean
(tag [1 2 3])   ; => :vector
(tag '(1 2 3))  ; => :seq
(tag (range 3)) ; => :seq
(tag 1)         ; => :number
(tag 1.5)       ; => :number
(tag ##Inf)     ; => :number

lasertag.core/tag-map returns a map with more info:

(tag-map (range 3))
=>
{:tag       :seq
 :type      clojure.lang.LongRange
 :all-tags  #{:seqable
              :sequential
              :coll
              :deferred
              :coll-like
              :lazy
              :seq
              :list-like
              :carries-meta
              :range}
 :classname "clojure.lang.LongRange"}

Examples

lasertag.core/tag vs clojure.core/type

Input valuelasertag.core/tagclojure.core/type
"hi":stringjava.lang.String
:hi:keywordclojure.lang.Keyword
"^hi$":regexjava.util.regex.Pattern
true:booleanjava.lang.Boolean
mysym:symbolclojure.lang.Symbol
nil:nilnil
[1 2 3]:vectorclojure.lang.PersistentVector
#{1 3 2}:setclojure.lang.PersistentHashSet
{:a 2, :b 3}:mapclojure.lang.PersistentArrayMap
(map inc (range 3)):seqclojure.lang.LazySeq
(range 3):seqclojure.lang.LongRange
(:a :b :c):seqclojure.lang.PersistentList
Infinity:numberjava.lang.Double
-Infinity:numberjava.lang.Double
NaN:numberjava.lang.Double
1/3:numberclojure.lang.Ratio
(byte 0):numberjava.lang.Byte
(short 3):numberjava.lang.Short
(double 23.44):numberjava.lang.Double
1M:numberjava.math.BigDecimal
1:numberjava.lang.Long
(float 1.5):numberjava.lang.Float
(char a):charjava.lang.Character
(java.math.BigInteger. "171"):numberjava.math.BigInteger
(java.util.Date.):datetimejava.util.Date
java.util.Date:classjava.lang.Class

ClojureScript Examples

lasertag.core/tag vs cljs.core/type

Input valuelasertag.core/tagcljs.core/type
"hi":string#object[String]
:hi:keywordcljs.core/Keyword
"^hi$":regex#object[RegExp]
true:boolean#object[Boolean]
mysym:symbolcljs.core/Symbol
nil:nilnil
[1 2 3]:vectorcljs.core/PersistentVector
#{1 3 2}:setcljs.core/PersistentHashSet
{:a 2, :b 3}:mapcljs.core/PersistentArrayMap
(map inc (range 3)):seqcljs.core/LazySeq
(range 3):seqcljs.core/IntegerRange
(:a :b :c):seqcljs.core/List
Infinity:number#object[Boolean]
-Infinity:number#object[Boolean]
js/parseInt:function#object[Function]
(new js/Date.):datetime#object[Date]
(.values #js [1 2 3]):iterable#object[Object]
(array "a" "b"):array#object[Array]
(new js/Int8Array #js ["a" "b"]):array#object[Int8Array]
(new js/Set #js[1 2 3]):set#object[Set]
(js/Promise. (fn [x] x)):promise#object[Promise]


Setup

Requires Clojure 1.11.1 or higher

If using with Babashka, requires Babashka v1.12.210 or higher


Add as a dependency to your project:


Deps:

io.github.paintparty/lasertag {:mvn/version "0.13.0"}

Leiningen:

[io.github.paintparty/lasertag "0.13.0"]

Require it:

(require '[lasertag.core :refer [tag tag-map]])

Or import into your namespace:

(ns myns.core
  (:require
    [lasertag.core :refer [tag tag-map]]))


Usage

lasertag.core/tag returns a keyword describing the type of value:

(tag 1) ;; => :number

lasertag.core/tag-map will return a map with additional info.

;; string

(tag-map "hi")
=>
{:tag       :string
 :type      java.lang.String
 :all-tags  #{:string :scalar}
 :classname "java.lang.String"}



;; map 

(tag-map {:a :foo})
=>
{:tag       :map
 :type      clojure.lang.PersistentArrayMap
 :all-tags  #{:callable
              :seqable
              :editable
              :associative
              :coll
              :array-map
              :coll-like
              :map-like
              :map
              :carries-meta}
 :classname "clojure.lang.PersistentArrayMap"}



;; range 

(tag-map (range 3))
=>
{:tag       :seq
 :type      clojure.lang.LongRange
 :all-tags  #{:seqable
              :sequential
              :coll
              :deferred
              :coll-like
              :lazy
              :seq
              :list-like
              :carries-meta
              :range}
 :classname "clojure.lang.LongRange"}



;; Record

(defrecord MyRecordType [a b c d])
(tag-map (->MyRecordType 4 8 4 5)) 
=>
{:tag       :record
 :type      my.ns.MyRecordType
 :all-tags  #{:datatype
              :coll
              :coll-like
              :record
              :map-like
              :carries-meta}
 :classname "my.ns.MyRecordType"}



;; Delay

(def my-delay (delay 100))
(tag-map my-delay)
=>
{:tag       :delay
 :type      clojure.lang.Delay
 :all-tags  #{:deferred :delay :derefable}
 :classname "clojure.lang.Delay"}



;; Multimethod definition

(defmulti different-behavior (fn [x] (:x-type x)))
(tag-map different-behavior)
=>
{:tag       :function
 :type      clojure.lang.MultiFn
 :all-tags  #{:multi-function :function :callable}
 :classname "clojure.lang.MultiFn"}



;; Var

(def bar nil)
(tag-map #'bar)
=>
{:tag       :var
 :type      clojure.lang.Var
 :all-tags  #{:reference :var :callable :derefable}
 :classname "clojure.lang.Var"}






Performance

Based on criterium quick-bench testing, most values will return in 15 ~ 50 nanoseconds, depending on hardware. More exotic values whose types are not present in Lasertag's pre-generated result-by-class map will return in 10µs ~ 50µs, depending on hardware.


Test

The JVM tests require leiningen to be installed.

lein test

ClojureScript tests:

npm run test

Babashka tests:

bb test:bb

Developing

Most of the tests are auto-generated.

CLJ, bb testing

To regenerate tests for JVM Clojure, look at source of lasertag.cached.

CLJS testing

The cljs tests can also be regenerated.

Additionally, the data structure that serves most of the results for lasertag.core/tag & lasertag.core/tag-map in cljs needs to be pre-generated.

For both of these:

  1. Toggle lasertag.cljs.codegen/write-tests? and lasertag.jsi.codegen/write-classes? to true.

  2. In one terminal: npm run codegen

  3. In another terminal: npm run codegen-watch

  4. The test and classes should immediately get generated and written to disc. Kill the npm run codegen-watch process.

  5. Toggle the defs from step 1 back to false


Status

Alpha, subject to change. Issues welcome, see contributing.


Contributing

Issues for bugs, improvements, or features are very welcome. Please file an issue for discussion before starting or issuing a PR.


License

Copyright © 2024-2026 Jeremiah Coyle

This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.

This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close