### Install Lentes with Leiningen or deps.edn Source: https://context7.com/funcool/lentes/llms.txt Add the Lentes library to your project dependencies using either Leiningen or Clojure CLI's deps.edn. ```clojure ;; project.clj (Leiningen) [funcool/lentes "1.3.3"] ;; deps.edn {:deps {funcool/lentes {:mvn/version "1.3.3"}}} ``` -------------------------------- ### Create and Use a Custom Lens Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Define a lens using custom getter and setter functions and then use `l/focus` to get and `l/over` to modify the focused value. ```clojure (require '[lentes.core :as l]) (def mylens (l/lens my-getter my-setter)) (l/focus mylens data) ;; => 1 (l/over mylens inc data) ;; => {:foo 2 :bar 2} ``` -------------------------------- ### Use Custom Getter and Setter Functions Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Demonstrates how to use custom getter and setter functions to access and modify data within a map. ```clojure (def data {:foo 1 :bar 2}) (my-getter data) ;; => 1 (my-setter data inc) ;; => {:foo 2 :bar 2} ``` -------------------------------- ### Running Lentes Tests on Node.js Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Instructions for executing Lentes tests on the Node.js platform. ```text clojure -Adev tools build:tests node ./target/tests.js ``` -------------------------------- ### Create a Lens using `l/key` Helper Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Utilize the `l/key` helper function to create a lens that focuses on a specific key within an associative data structure. ```clojure (def mylens (l/key :foo)) (l/focus mylens data) ;; => 1 (l/over mylens inc data) ;; => {:foo 2 :bar 2} ``` -------------------------------- ### Defining and Using Unit Conversion Lenses Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Shows how to create a lens for unit conversion by providing functions for converting between two units in both directions. ```clojure (defn sec->min [sec] (/ sec 60)) (defn min->sec [min] (* min 60)) (def mins (l/units sec->min min->sec)) (l/focus mins 120) ;; => 2 (l/put mins 3 120) ;; => 180 (l/over mins inc 60) ;; => 120 ``` -------------------------------- ### Running Lentes Tests on JVM Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Instructions for executing Lentes tests on the JVM platform. ```text clojure -Adev -m lentes.tests ``` -------------------------------- ### Modify Data with `l/put` and `l/over` using `l/id` Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Demonstrates using `l/put` to replace the entire data structure and `l/over` to apply a function to it, using the `l/id` lens. ```clojure (l/put l/id 42 [0 1 2 3]) ;; => 42 (l/over l/id count [0 1 2 3]) ;; => 4 ``` -------------------------------- ### in Source: https://context7.com/funcool/lentes/llms.txt Creates a lens that navigates a nested associative structure using `get-in` / `update-in` semantics. Accepts an optional default value for missing paths. ```APIDOC ## `in` — Lens into a nested path Creates a lens that navigates a nested associative structure using `get-in` / `update-in` semantics. Accepts an optional default value for missing paths. ```clojure (def cfg {:db {:host "localhost" :port 5432}}) (l/focus (l/in [:db :host]) cfg) ;; => "localhost" (l/over (l/in [:db :port]) inc cfg) ;; => {:db {:host "localhost", :port 5433}} (l/put (l/in [:db :host]) "db.prod" cfg) ;; => {:db {:host "db.prod", :port 5432}} ;; With a default for missing keys (l/focus (l/in [:db :timeout] 30) cfg) ;; => 30 ``` ``` -------------------------------- ### Construct a Custom Lens with `lens` Source: https://context7.com/funcool/lentes/llms.txt Create a custom lens using a getter and setter function. A getter-only function creates a read-only lens. ```clojure (require '[lentes.core :as l]) ;; Read-write lens focusing on :score inside a map (def score-lens (l/lens (fn [state] (:score state)) (fn [state f] (update state :score f)))) (def game {:player "Alice" :score 100}) (l/focus score-lens game) ;; => 100 (l/over score-lens + game 50) ; apply (+ 100 50) (l/over score-lens (partial + 50) game) ;; => {:player "Alice", :score 150} (l/put score-lens 0 game) ;; => {:player "Alice", :score 0} ;; Read-only lens (throws on write) (def name-lens (l/lens :player)) (l/focus name-lens game) ;; => "Alice" ``` -------------------------------- ### Focusing on Specific Keys in Associative Data Structures Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Demonstrates how to focus on, over, or put values associated with specific keys in a map. Supports selecting single or multiple keys. ```clojure (l/focus (l/key :a) {:a 1 :b 2}) ;; => 1 ``` ```clojure (l/over (l/key :a) str {:a 1 :b 2}) ;; => {:a "1", :b 2} ``` ```clojure (l/put (l/key :a) 42 {:a 1 :b 2}) ;; => {:a 42, :b 2} ``` ```clojure (l/focus (l/select-keys [:a]) {:a 1 :b 2}) ;; => {:a 1} ``` ```clojure (l/over (l/select-keys [:a :c]) (fn [m] (zipmap (keys m) (repeat 42))) {:a 1 :b 2}) ;; => {:b 2, :a 42} ``` ```clojure (l/put (l/select-keys [:a :c]) {:a 0} {:a 1 :b 2 :c 42}) ;; => {:b 2, :a 0} ``` -------------------------------- ### Composing Lenses with Function Composition Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Explains how lenses can be composed using standard function composition, allowing for complex data transformations. ```clojure (def my-lens (comp l/fst (l/nth 2))) (def data [[0 1 2] [3 4 5]]) (l/focus my-lens data) ;; => 2 (l/put my-lens 42 data) ;; => [[0 1 42] [3 4 5]] ``` -------------------------------- ### Select Multiple Keys with `select-keys` Lens Source: https://context7.com/funcool/lentes/llms.txt Use `select-keys` to create a lens that focuses on a sub-map containing only the specified keys. Updates to this sub-map only affect these keys, leaving the rest of the original map unchanged. ```clojure (def record {:a 1 :b 2 :c 3}) (l/focus (l/select-keys [:a :b]) record) ;; => {:a 1, :b 2} (l/over (l/select-keys [:a :b]) (fn [m] (update-vals m * 10)) record) ;; => {:a 10, :b 20, :c 3} (l/put (l/select-keys [:a :b]) {:a 0 :b 0} record) ;; => {:a 0, :b 0, :c 3} ``` -------------------------------- ### Cloning the Lentes Repository Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Provides the command to clone the Lentes open-source project from its GitHub repository. ```text git clone https://github.com/funcool/lentes ``` -------------------------------- ### Focus with the Identity Lens (`l/id`) Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Use the `l/id` lens to focus on the entire data structure itself. The `l/focus` function returns the data unchanged. ```clojure (require '[lentes.core :as l]) (l/focus l/id [0 1 2 3]) ;; => [0 1 2 3] ``` -------------------------------- ### Focusing on Nested Data Structures Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Illustrates how to access and modify deeply nested data structures using paths defined by sequences of keys. ```clojure (l/focus (l/in [:a :b]) {:a {:b {:c 42}}}) ;; => {:c 42} ``` ```clojure (l/over (l/in [:a :b]) #(zipmap (vals %) (keys %)) {:a {:b {:c 42}}}) ;; => {:a {:b {42 :c}}} ``` ```clojure (l/put (l/in [:a :b]) 42 {:a {:b {:c 42}}}) ;; => {:a {:b 42}} ``` -------------------------------- ### Define Custom Getter and Setter Functions Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Create custom functions for retrieving and updating specific parts of a data structure. These can be used to build custom lenses. ```clojure (defn my-getter [state] (:foo state)) (defn my-setter [state f] (update state :foo f)) ``` -------------------------------- ### Focusing Using Conditional Lenses Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Demonstrates conditional focusing where a lens only applies its operation if a given predicate function returns true for the value. ```clojure (l/focus (l/passes even?) 2) ;; => 2 ``` ```clojure (l/over (l/passes even?) inc 2) ;; => 3 ``` ```clojure (l/put (l/passes even?) 42 2) ;; => 42 ``` ```clojure (l/focus (l/passes even?) 1) ;; => nil ``` ```clojure (l/over (l/passes even?) inc 1) ;; => 1 ``` ```clojure (l/put (l/passes even?) 42 1) ;; => 1 ``` -------------------------------- ### Create Derived Atoms with `l/derive` Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Use `l/derive` to create new atoms that are derived from an existing atom, focusing on a specific part of its state. These derived atoms are lazy and smart, only triggering updates when the focused data changes. ```clojure (def state1 (atom {:foo 1 :bar 1})) (def state2 (l/derive (l/key :foo) state1)) (satisfies? IAtom state2) ;; => true @state2 ;; => 1 (swap! state2 inc) @state2 ;; => 2 @state1 ;; => {:foo 2 :bar 2} ``` -------------------------------- ### key — Lens into an associative key Source: https://context7.com/funcool/lentes/llms.txt Creates a lens that focuses on a specific key in a map. ```APIDOC ## key — Lens into an associative key Creates a lens that focuses on a specific key in a map. ### Usage ```clojure (def data {:name "Carol" :age 30 :active true}) (l/focus (l/key :name) data) ;; => "Carol" (l/over (l/key :age) inc data) ;; => {:name "Carol", :age 31, :active true} (l/put (l/key :active) false data) ;; => {:name "Carol", :age 30, :active false} ``` ``` -------------------------------- ### select-keys Source: https://context7.com/funcool/lentes/llms.txt Focuses on a sub-map formed by the provided keys. Updates apply only to those keys; the rest of the map is unchanged. ```APIDOC ## `select-keys` — Lens over multiple keys Focuses on a sub-map formed by the provided keys. Updates apply only to those keys; the rest of the map is unchanged. ```clojure (def record {:a 1 :b 2 :c 3}) (l/focus (l/select-keys [:a :b]) record) ;; => {:a 1, :b 2} (l/over (l/select-keys [:a :b]) (fn [m] (update-vals m * 10)) record) ;; => {:a 10, :b 20, :c 3} (l/put (l/select-keys [:a :b]) {:a 0 :b 0} record) ;; => {:a 0, :b 0, :c 3} ``` ``` -------------------------------- ### Sequence Position Lenses (`fst`, `snd`, `nth`) Source: https://context7.com/funcool/lentes/llms.txt These lenses access elements by their position in indexed collections. `fst` targets the first, `snd` the second, and `(l/nth n)` targets the element at index `n`. ```clojure (l/focus l/fst [:a :b :c]) ;; => :a (l/focus l/snd [:a :b :c]) ;; => :b (l/focus (l/nth 2) [:a :b :c]) ;; => :c (l/over l/fst name [:foo :bar]) ;; => ["foo" :bar] (l/put (l/nth 2) :X [:a :b :c]) ;; => [:a :b :X] ;; Nested: focus on first element of first sub-vector (def ffst (comp l/fst l/fst)) (l/focus ffst [[1 2] [3 4]]) ;; => 1 (l/put ffst 99 [[1 2] [3 4]]) ;; => [[99 2] [3 4]] ``` -------------------------------- ### over — Apply a function through a lens Source: https://context7.com/funcool/lentes/llms.txt Given a lens, a function, and a state, applies the function to the focused value and returns the updated state without mutation. ```APIDOC ## over — Apply a function through a lens Given a lens, a function, and a state, applies the function to the focused value and returns the updated state — pure, no mutation. ### Usage ```clojure (l/over l/fst inc [1 2 3]) ;; => [2 2 3] (l/over (l/key :count) inc {:count 5 :name "x"}) ;; => {:count 6, :name "x"} (l/over (l/in [:config :retries]) (partial * 2) {:config {:retries 3}}) ;; => {:config {:retries 6}} (l/over (l/nth 1) str [10 20 30]) ;; => [10 "20" 30] ``` ``` -------------------------------- ### Unit Conversion with `units` Lens Source: https://context7.com/funcool/lentes/llms.txt The `units` lens provides a bidirectional conversion between two representations using provided `a->b` and `b->a` functions. `focus` returns the value in unit B, and updates are applied in unit B before being converted back to unit A. ```clojure (defn km->miles [km] (* km 0.621371)) (defn miles->km [mi] (/ mi 0.621371)) (def miles-lens (l/units km->miles miles->km)) (l/focus miles-lens 100) ;; => 62.1371 (100 km in miles) (l/put miles-lens 62.1371 100) ;; => ~100.0 (store 62.1371 miles back as km) (l/over miles-lens (partial + 10) 100) ;; add 10 miles, stored as km ;; Time unit example (from the docs) (def mins (l/units #(/ % 60) #(* % 60))) (l/focus mins 120) ;; => 2 (120 sec = 2 min) (l/put mins 3 120) ;; => 180 (3 min → 180 sec) (l/over mins inc 60) ;; => 120 (1 min + 1 = 2 min = 120 sec) ``` -------------------------------- ### Navigate Nested Structures with `in` Lens Source: https://context7.com/funcool/lentes/llms.txt The `in` lens facilitates navigation into nested associative structures, mimicking `get-in` and `update-in` semantics. It supports an optional default value for paths that do not exist. ```clojure (def cfg {:db {:host "localhost" :port 5432}}) (l/focus (l/in [:db :host]) cfg) ;; => "localhost" (l/over (l/in [:db :port]) inc cfg) ;; => {:db {:host "localhost", :port 5433}} (l/put (l/in [:db :host]) "db.prod" cfg) ;; => {:db {:host "db.prod", :port 5432}} ;; With a default for missing keys (l/focus (l/in [:db :timeout] 30) cfg) ;; => 30 ``` -------------------------------- ### passes Source: https://context7.com/funcool/lentes/llms.txt Creates a lens that focuses on a value only if it satisfies the given predicate. If the predicate fails, `focus` returns `nil` and `over`/`put` return the state unchanged. ```APIDOC ## `passes` — Conditional lens Creates a lens that focuses on a value only if it satisfies the given predicate. If the predicate fails, `focus` returns `nil` and `over`/`put` return the state unchanged. Note this lens is not fully well-behaved; its behavior depends on the predicate outcome. ```clojure (def positive? (l/passes pos?)) (l/focus positive? 5) ;; => 5 (l/focus positive? -1) ;; => nil (l/over positive? inc 5) ;; => 6 (l/over positive? inc -1) ;; => -1 (predicate failed, no change) (l/put positive? 100 5) ;; => 100 (l/put positive? 100 -1) ;; => -1 (unchanged) ;; Compose with fst: only update first element if it's odd (def fst-if-odd (comp l/fst (l/passes odd?))) (l/over fst-if-odd inc [3 2 1]) ;; => [4 2 1] (l/over fst-if-odd inc [2 3 4]) ;; => [2 3 4] (2 is even, no change) ``` ``` -------------------------------- ### Apply Function with `over` Source: https://context7.com/funcool/lentes/llms.txt The `over` function applies a given function to the value focused by a lens, returning a new state with the updated value. ```clojure (l/over l/fst inc [1 2 3]) ;; => [2 2 3] (l/over (l/key :count) inc {:count 5 :name "x"}) ;; => {:count 6, :name "x"} (l/over (l/in [:config :retries]) (partial * 2) {:config {:retries 3}}) ;; => {:config {:retries 6}} (l/over (l/nth 1) str [10 20 30]) ;; => [10 "20" 30] ``` -------------------------------- ### Tail Lens (`tail`) Source: https://context7.com/funcool/lentes/llms.txt The `l/tail` lens focuses on all elements of a collection except the first. When updating, it preserves the original head of the collection. ```clojure (l/focus l/tail [1 2 3 4]) ;; => (2 3 4) (l/over l/tail (partial map inc) [1 2 3 4]) ;; => (1 3 4 5) (l/put l/tail [20 30] [1 2 3]) ;; => (1 20 30) ``` -------------------------------- ### lens — Construct a custom lens Source: https://context7.com/funcool/lentes/llms.txt Creates a lens from a getter and setter function. A read-only lens can be created by providing only a getter. ```APIDOC ## lens — Construct a custom lens Creates a lens from a getter function and a setter function. The getter extracts a focused value from a state; the setter takes the state and an update function and returns a new state. Passing only a getter produces a read-only lens that throws if a write is attempted. ### Usage ```clojure (require '[lentes.core :as l]) ;; Read-write lens focusing on :score inside a map (def score-lens (l/lens (fn [state] (:score state)) (fn [state f] (update state :score f)))) (def game {:player "Alice" :score 100}) (l/focus score-lens game) ;; => 100 (l/over score-lens + game 50) ;; => {:player "Alice", :score 150} (l/put score-lens 0 game) ;; => {:player "Alice", :score 0} ;; Read-only lens (throws on write) (def name-lens (l/lens :player)) (l/focus name-lens game) ;; => "Alice" ``` ``` -------------------------------- ### derive Source: https://context7.com/funcool/lentes/llms.txt Creates a derived atom from an existing atom using a lens. The derived atom is lazy, smart, and fully implements the atom interface for read-write or optionally read-only access. ```APIDOC ## `derive` — Create a derived atom (materialized view) Creates a derived atom from an existing atom using a lens. The derived atom is lazy (no computation until dereferenced), smart (only fires watchers when the focused slice actually changes), and fully implements the atom interface for read-write or optionally read-only access. ```clojure ;; Read-write derived atom (def app-state (atom {:user {:name "Alice" :role "admin"} :settings {:theme "dark" :lang "en"}})) (def user-atom (l/derive (l/key :user) app-state)) (def theme-atom (l/derive (l/in [:settings :theme]) app-state)) @user-atom ;; => {:name "Alice", :role "admin"} @theme-atom ;; => "dark" (swap! user-atom assoc :role "superadmin") @app-state ;; => {:user {:name "Alice", :role "superadmin"}, :settings {:theme "dark", :lang "en"}} (reset! theme-atom "light") @app-state ;; => {:user {:name "Alice", :role "superadmin"}, :settings {:theme "light", :lang "en"}} ;; Read-only derived ref (satisfies IDeref + IWatchable, not IAtom) (def ro-name (l/derive (l/in [:user :name]) app-state {:read-only? true})) @ro-name ;; => "Alice" ;; (reset! ro-name "Bob") would throw ;; Watch the derived atom — fires only when focused slice changes (add-watch theme-atom :logger (fn [key ref old-val new-val] (println "Theme changed:" old-val "->" new-val))) (swap! app-state assoc-in [:settings :lang] "fr") ;; no watch fired (theme unchanged) (reset! theme-atom "solarized") ;; fires :logger watch ;; Use value equality instead of identity for change detection (def eq-atom (l/derive (l/key :user) app-state {:equals? =})) ;; IAtom2 support (Clojure JVM only) — reset-vals! / swap-vals! (swap-vals! user-atom assoc :verified true) ;; => [{:name "Alice", :role "superadmin"} {:name "Alice", :role "superadmin", :verified true}] ``` ``` -------------------------------- ### Key Lens (`key`) Source: https://context7.com/funcool/lentes/llms.txt The `l/key` lens is used to focus on a specific key within an associative data structure like a map. ```clojure (def data {:name "Carol" :age 30 :active true}) (l/focus (l/key :name) data) ;; => "Carol" (l/over (l/key :age) inc data) ;; => {:name "Carol", :age 31, :active true} (l/put (l/key :active) false data) ;; => {:name "Carol", :age 30, :active false} ``` -------------------------------- ### Sequence Lens: `l/fst` Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Use the `l/fst` lens to access, modify, or replace the first element of a sequence (e.g., a vector). ```clojure ;; Focus over the first element of a vector (l/focus l/fst [1 2 3]) ;; => 1 ;; Apply a function over first element of a vector (l/over l/fst inc [1 2 3]) ;; => [2 2 3] ;; Replace the first value of an element of a vector (l/put l/fst 42 [1 2 3]) ;; => [42 2 3] ``` -------------------------------- ### focus — Read through a lens Source: https://context7.com/funcool/lentes/llms.txt Given a lens and a state value, returns the value the lens is focused on. ```APIDOC ## focus — Read through a lens Given a lens and a state value, returns the value the lens is focused on. ### Usage ```clojure (l/focus l/id [1 2 3]) ;; => [1 2 3] (l/focus l/fst [10 20 30]) ;; => 10 (l/focus (l/key :x) {:x 7 :y 8}) ;; => 7 (l/focus (l/in [:a :b]) {:a {:b 42}}) ;; => 42 (l/focus (l/nth 2) [:a :b :c]) ;; => :c ``` ``` -------------------------------- ### units Source: https://context7.com/funcool/lentes/llms.txt Constructs a bidirectional lens that converts between two representations. Given `a->b` and `b->a` functions, `focus` returns the value in unit B and updates are applied in unit B then converted back to unit A. ```APIDOC ## `units` — Unit-conversion lens Constructs a bidirectional lens that converts between two representations. Given `a->b` and `b->a` functions, `focus` returns the value in unit B and updates are applied in unit B then converted back to unit A. ```clojure (defn km->miles [km] (* km 0.621371)) (defn miles->km [mi] (/ mi 0.621371)) (def miles-lens (l/units km->miles miles->km)) (l/focus miles-lens 100) ;; => 62.1371 (100 km in miles) (l/put miles-lens 62.1371 100) ;; => ~100.0 (store 62.1371 miles back as km) (l/over miles-lens (partial + 10) 100) ;; add 10 miles, stored as km ;; Time unit example (from the docs) (def mins (l/units #(/ % 60) #(* % 60))) (l/focus mins 120) ;; => 2 (120 sec = 2 min) (l/put mins 3 120) ;; => 180 (3 min → 180 sec) (l/over mins inc 60) ;; => 120 (1 min + 1 = 2 min = 120 sec) ``` ``` -------------------------------- ### Compose Lenses for Nested Access Source: https://context7.com/funcool/lentes/llms.txt Compose lenses using `clojure.core/comp` to focus on specific elements within nested data structures. The leftmost lens operates first. Ensure correct usage of `partial` for functions with multiple arguments when using `l/over`. ```clojure ;; Focus on the third element of the first sub-vector (def first-row-third-col (comp l/fst (l/nth 2))) (def matrix [[10 20 30] [40 50 60]]) (l/focus first-row-third-col matrix) ;; => 30 (l/put first-row-third-col 99 matrix) ;; => [[10 20 99] [40 50 60]] (l/over first-row-third-col * 2 matrix) ;; err — use (partial * 2) (l/over first-row-third-col (partial * 2) matrix) ;; => [[10 20 60] [40 50 60]] ``` ```clojure ;; Compose conditional with positional lens (def fst-even (comp l/fst (l/passes even?))) (l/focus fst-even [4 5 6]) ;; => 4 (l/focus fst-even [3 5 7]) ;; => nil ``` ```clojure ;; Deep path via composition of key lenses (def deep (comp (l/key :a) (l/key :b) (l/key :c))) (l/focus deep {:a {:b {:c 42}}}) ;; => 42 (l/put deep 0 {:a {:b {:c 42}}}) ;; => {:a {:b {:c 0}}} ``` -------------------------------- ### Require Lentes Core Namespace Source: https://context7.com/funcool/lentes/llms.txt Import the core Lentes namespace for use in your Clojure code. ```clojure (require '[lentes.core :as l]) ``` -------------------------------- ### tail — Lens over the tail of a collection Source: https://context7.com/funcool/lentes/llms.txt Focuses on all elements of a collection except the first, preserving the head on updates. ```APIDOC ## tail — Lens over the tail of a collection Focuses on all elements of a collection except the first, preserving the head on updates. ### Usage ```clojure (l/focus l/tail [1 2 3 4]) ;; => (2 3 4) (l/over l/tail (partial map inc) [1 2 3 4]) ;; => (1 3 4 5) (l/put l/tail [20 30] [1 2 3]) ;; => (1 20 30) ``` ``` -------------------------------- ### Identity Lens (`id`) Source: https://context7.com/funcool/lentes/llms.txt The `l/id` lens focuses on the entire data structure. It's useful as a base case for composition or for no-op operations. ```clojure (l/focus l/id {:a 1}) ;; => {:a 1} (l/put l/id 42 [1 2 3]) ;; => 42 (l/over l/id count [1 2 3]) ;; => 3 ``` -------------------------------- ### Create Derived Atoms with `derive` Source: https://context7.com/funcool/lentes/llms.txt The `derive` function creates a derived atom (materialized view) from an existing atom using a lens. Derived atoms are lazy, efficiently update watchers only when the focused slice changes, and fully implement the atom interface for read-write or read-only access. ```clojure ;; Read-write derived atom (def app-state (atom {:user {:name "Alice" :role "admin"} :settings {:theme "dark" :lang "en"}})) (def user-atom (l/derive (l/key :user) app-state)) (def theme-atom (l/derive (l/in [:settings :theme]) app-state)) @user-atom ;; => {:name "Alice", :role "admin"} @theme-atom ;; => "dark" (swap! user-atom assoc :role "superadmin") @app-state ;; => {:user {:name "Alice", :role "superadmin"}, :settings {:theme "dark", :lang "en"}} (reset! theme-atom "light") @app-state ;; => {:user {:name "Alice", :role "superadmin"}, :settings {:theme "light", :lang "en"}} ;; Read-only derived ref (satisfies IDeref + IWatchable, not IAtom) (def ro-name (l/derive (l/in [:user :name]) app-state {:read-only? true})) @ro-name ;; => "Alice" ;; (reset! ro-name "Bob") would throw ;; Watch the derived atom — fires only when focused slice changes (add-watch theme-atom :logger (fn [key ref old-val new-val] (println "Theme changed:" old-val "->" new-val))) (swap! app-state assoc-in [:settings :lang] "fr") ;; no watch fired (theme unchanged) (reset! theme-atom "solarized") ;; fires :logger watch ;; Use value equality instead of identity for change detection (def eq-atom (l/derive (l/key :user) app-state {:equals? =})) ;; IAtom2 support (Clojure JVM only) — reset-vals! / swap-vals! (swap-vals! user-atom assoc :verified true) ;; => [{:name "Alice", :role "superadmin"} {:name "Alice", :role "superadmin", :verified true}] ``` -------------------------------- ### fst / snd / nth — Sequence position lenses Source: https://context7.com/funcool/lentes/llms.txt `fst` focuses on the first element, `snd` on the second, and `(l/nth n)` on index `n` of any indexed collection. ```APIDOC ## fst / snd / nth — Sequence position lenses `fst` focuses on the first element, `snd` on the second, and `(l/nth n)` on index `n` of any indexed collection. ### Usage ```clojure (l/focus l/fst [:a :b :c]) ;; => :a (l/focus l/snd [:a :b :c]) ;; => :b (l/focus (l/nth 2) [:a :b :c]) ;; => :c (l/over l/fst name [:foo :bar]) ;; => ["foo" :bar] (l/put (l/nth 2) :X [:a :b :c]) ;; => [:a :b :X] ;; Nested: focus on first element of first sub-vector (def ffst (comp l/fst l/fst)) (l/focus ffst [[1 2] [3 4]]) ;; => 1 (l/put ffst 99 [[1 2] [3 4]]) ;; => [[99 2] [3 4]] ``` ``` -------------------------------- ### Sequence Lens: `l/nth` Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Use the `l/nth` lens to focus on, modify, or replace an element at a specific index within a sequence. The index is provided as an argument to `l/nth`. ```clojure (l/focus (l/nth 2) [1 2 3]) ;; => 3 (l/over (l/nth 2) inc [1 2 3]) ;; => [1 2 4] (l/put (l/nth 2) 42 [1 2 3]) ;; => [1 2 42] ``` -------------------------------- ### Read Focused Value with `focus` Source: https://context7.com/funcool/lentes/llms.txt Use the `focus` function to extract the value a lens is targeting within a given state. ```clojure (l/focus l/id [1 2 3]) ;; => [1 2 3] (l/focus l/fst [10 20 30]) ;; => 10 (l/focus (l/key :x) {:x 7 :y 8}) ;; => 7 (l/focus (l/in [:a :b]) {:a {:b 42}}) ;; => 42 (l/focus (l/nth 2) [:a :b :c]) ;; => :c ``` -------------------------------- ### put — Replace a focused value through a lens Source: https://context7.com/funcool/lentes/llms.txt Given a lens, a new value, and a state, replaces the focused value with the new one and returns the updated state. ```APIDOC ## put — Replace a focused value through a lens Given a lens, a new value, and a state, replaces the focused value with the new one and returns the updated state. ### Usage ```clojure (l/put l/fst :zero [1 2 3]) ;; => [:zero 2 3] (l/put (l/key :status) :active {:id 1 :status :pending}) ;; => {:id 1, :status :active} (l/put (l/in [:user :role]) "admin" {:user {:name "Bob" :role "guest"}}) ;; => {:user {:name "Bob", :role "admin"}} (l/put (l/nth 0) 99 [0 1 2]) ;; => [99 1 2] ``` ``` -------------------------------- ### Add Lentes Dependency to project.clj Source: https://github.com/funcool/lentes/blob/master/doc/content.adoc Include the Lentes library in your Clojure project by adding its coordinates to the dependency vector in your project.clj file. ```clojure [funcool/lentes "1.3.3"] ``` -------------------------------- ### Replace Value with `put` Source: https://context7.com/funcool/lentes/llms.txt Use `put` to replace the value focused by a lens with a new value, returning the modified state. ```clojure (l/put l/fst :zero [1 2 3]) ;; => [:zero 2 3] (l/put (l/key :status) :active {:id 1 :status :pending}) ;; => {:id 1, :status :active} (l/put (l/in [:user :role]) "admin" {:user {:name "Bob" :role "guest"}}) ;; => {:user {:name "Bob", :role "admin"}} (l/put (l/nth 0) 99 [0 1 2]) ;; => [99 1 2] ``` -------------------------------- ### Conditional Focusing with `passes` Lens Source: https://context7.com/funcool/lentes/llms.txt Use the `passes` lens to focus on a value only if it satisfies a given predicate. If the predicate fails, `focus` returns `nil`, and `over`/`put` operations leave the state unchanged. This lens is not fully well-behaved as its outcome depends on the predicate. ```clojure (def positive? (l/passes pos?)) (l/focus positive? 5) ;; => 5 (l/focus positive? -1) ;; => nil (l/over positive? inc 5) ;; => 6 (l/over positive? inc -1) ;; => -1 (predicate failed, no change) (l/put positive? 100 5) ;; => 100 (l/put positive? 100 -1) ;; => -1 (unchanged) ;; Compose with fst: only update first element if it's odd (def fst-if-odd (comp l/fst (l/passes odd?))) (l/over fst-if-odd inc [3 2 1]) ;; => [4 2 1] (l/over fst-if-odd inc [2 3 4]) ;; => [2 3 4] (2 is even, no change) ``` -------------------------------- ### id — Identity lens Source: https://context7.com/funcool/lentes/llms.txt The identity lens focuses on the entire data structure as-is. It's useful as a base case in composition or for no-op operations. ```APIDOC ## id — Identity lens The identity lens focuses on the entire data structure as-is. Useful as a base case in composition or when you need a no-op lens. ### Usage ```clojure (l/focus l/id {:a 1}) ;; => {:a 1} (l/put l/id 42 [1 2 3]) ;; => 42 (l/over l/id count [1 2 3]) ;; => 3 ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.