Liking cljdoc? Tell your friends :D

Lasertag

A library for categorizing values in Clojure dialects*
*Works with Clojure, ClojureScript, and Babashka



Examples  •  Setup  •  Usage  •  Performance  •  Testing  •  Development


Lasertag on Clojars


Why

Lasertag took shape while developing the syntax-colorizing hifi print engine used by Bling and Fireworks. This kind of "loose type system" is essential for any library or subsystem that does linting, syntax coloring, or similar.


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]]))

Consuming via git

Using a tool without Maven support? See Consuming Lasertag as a git dependency.



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"}




Primary Tags

List of all primary tags:

[:agent
 :array
 :atom
 :boolean
 :char
 :datetime
 :delay
 :function
 :keyword
 :list
 :map
 :nil
 :number
 :queue
 :reader-conditional
 :ref
 :regex
 :seq
 :set
 :string
 :symbol
 :throwable
 :uuid
 :var
 :vector
 :volatile]

List of all secondary tags (these show up in the :all-tags entry):

[:array-like
 :associative
 :big-decimal
 :big-int
 :byte
 :callable
 :carries-meta
 :char-sequence
 :coll
 :coll-like
 :cons
 :def
 :deferred
 :double
 :editable
 :error
 :exception
 :float
 :fractional
 :generator
 :global-this
 :hash-map
 :infinite
 :infinity
 :-infinity
 :inst
 :int
 :iterable
 :js
 :lazy
 :list-like
 :long
 :map-entry
 :map-like
 :multi-function
 :named
 :nan
 :nat-int
 :neg
 :neg-int
 :object
 :pos
 :pos-int
 :promise
 :range
 :ratio
 :real
 :record
 :reference
 :scalar
 :seqable
 :sequential
 :set-like
 :short
 :sorted
 :stack
 :subvec
 :transient
 :typed-array
 :whole
 :zero]

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.


Testing

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 (slightly clunky, this will be cleaned up in future):

  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