• Stars
    star
    145
  • Rank 248,958 (Top 5 %)
  • Language
    Clojure
  • License
    Other
  • Created almost 10 years ago
  • Updated over 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Multilenses for Clojure

Traversy

Build Status

Clojars Project

An experimental encoding of multilenses in Clojure.

What are multilenses?

Simply put, multilenses are generalisations of sequence and update-in. Traversy's view and update accept a lens that determines how values are extracted or updated.

update-in provides a way to apply a function within a nested map:

(-> {:x 2 :y 4} (update-in [:x] inc))
=> {:x 3 :y 4}

This works great so long as the value you want to update can be addressed by a single path. However, there is no function for updating every value in a map in clojure.core. Here's how it looks with the all-values lens:

(require ['traversy.lens :refer :all])
    
(-> {:x 2 :y 4} (update all-values inc))
=> {:x 3 :y 5}

The same lens can also be used for viewing the foci:

(-> {:x 2 :y 4} (view all-values))
=> (2 4)

Lenses can be easily composed, so it's easy to build one that suits your particular data structure:

(-> [{:x 1 :y 2} {:x 2 :y 7}] (update (*> each all-values) inc))
=> ({:x 2 :y 3} {:x 3 :y 8})

And as viewing also composes:

(-> [{:x 1 :y 2} {:x 2 :y 7}] (view (*> each all-values)))
=> (1 2 2 7)

As lenses are first class, once you have one that suits your needs, you can name it and put it in a var.

Usage

See the examples.

There is API documentation, which describes the operations and provided lenses. This was generated by Codox.

Background

At the 2014 Clojure eXchange I gave a talk about Lenses in general, and Traversy specifically.

Laws

Lenses follow some rules that make them behave intuitively. The first two rules are the Traversal Laws. The final rule governs the relationship between update and view.

An update has no effect if passed the identity function:

(-> x (update l identity)) === x

Fusing two updates together is the same as applying them separately:

(-> x (update l f1) (update l f2)) === (-> (update l (comp f2 f1)))

update then view is the same as view then map:

(-> x (update l f) (view l x)) === (->> x (view l) (map f))

These should hold for any lens l that applies to a data structure x.

The second rule can be violated when the foci of a lens change after an update. An example of this is when only is used with a predicate and function that interact.

These two expressions should have the same value, but as incrementing an odd number makes it even, the second update in the first example has no targets:

(-> [1 2 3] (update (only odd?) inc) (update (only odd?) inc)) => [2 2 4]
(-> [1 2 3] (update (only odd?) (comp inc inc))) => [3 2 5]

Careful when doing this - and please document any lenses that have this behaviour as unstable. Traversy comes with three unstable lenses: only, maybe and conditionally.

FAQs

Aren't these just degenerate Lenses?

Yes! In fact, they're degenerate Traversals, with the Foldable and Functor instances and without the generality of traversing using arbitrary Applicatives.

Will updates preserve the structure of the target?

Yes. Whether you focus on a map, a set, a vector or a sequence, the structure of the target will remain the same after an update.

Can I compose these Lenses with ordinary function composition?

No. Unlike Haskell Lenses, these are not represented as functions. You can, however, use combine (variadic form *>) and both (variadic form +>) to compose lenses.

Can I use Traversy with ClojureScript?

Yup!

How do I run the tests?

Clojure: lein test

ClojureScript: lein test-cljs (you'll need phantomjs)

both: lein test-all

Is this stable enough to use in production?

Traversy is in production use on the project it originated from, but the API may yet change.

More Repositories

1

klangmeister

A musical scratchpad.
Clojure
615
star
2

leipzig

A music composition library for Clojure and Clojurescript.
Clojure
451
star
3

functional-composition

A live-coding presentation on music theory and Bach's "Canone alla Quarta".
Clojure
164
star
4

goldberg

The Goldberg Variations in Overtone
Clojure
99
star
5

cljs-bach

A Clojurescript wrapper for the Web Audio API.
Clojure
97
star
6

kolmogorov-music

CSS
60
star
7

vim-fireplace-easy

A simple setup for getting started with Clojure and Vim.
Vim Script
56
star
8

whelmed

Clojure
56
star
9

african-polyphony-and-polyrhythm

Ethnomusicologists face a dilemma: either shoehorn African music into European notation, or create custom DSLs that can only be understood by a select band of European ethnomusicologists.
CSS
29
star
10

flying-spaghetti-monster

An Idris type provider for communicating type-checkable protocols.
Idris
26
star
11

leipzig-from-scratch

I show you how to make a simple track using Leipzig, Overtone and Clojure, from "lein new" onwards.
Clojure
21
star
12

Idris-Elba-Dev

A Whitespace-Based Dependently Typed Functional Programming Language
Haskell
16
star
13

birdsong-as-code

Exploring the music theory of birdsong
Clojure
14
star
14

kraftwerk

Covers of Kraftwerk tracks.
Clojure
13
star
15

journey-through-the-looking-glass

Talk at the Skillsmatter Clojure Exchange, 2014
Clojure
13
star
16

overtunes

Experiments with overtone.
Clojure
10
star
17

poker

A macro-based refactoring library for Clojure.
Clojure
6
star
18

dueling-keyboards

What did happen on the Cahulawassee River?
Clojure
6
star
19

leipzig-template

A simple template for new Leipzig tracks.
Clojure
6
star
20

cooking-with-clojure

Clojure
4
star
21

V

A Clojure validation library.
Clojure
4
star
22

klang

A Clojurescript wrapper for the Web Audio API.
Clojure
4
star
23

pea-brain

Neural nets from scratch
Clojure
3
star
24

leipzig-live

A musical scratchpad.
Clojure
3
star
25

analysis-by-compression

CSS
3
star
26

centurion

A hundred line Lisp.
Clojure
3
star
27

cadiz

Clojure
2
star
28

multibooks

Drupal module that allows nodes to live in more than one book. Not yet ready for use.
PHP
2
star
29

oncall-charter

A social contract for humane out-of-hours support.
2
star
30

rubyfridays

The material covered at Ruby Fridays for Girl Geek Kampala.
Ruby
1
star
31

strictly

Less tolerant versions of Clojure core features.
Clojure
1
star
32

vim

Personal vim setup.
Vim Script
1
star
33

all-i-wanted

Leipzig/Overtone tune
Clojure
1
star
34

elba

An Idris type provider for type-checked protocols.
Idris
1
star
35

in-the-mood

Clojure
1
star
36

sweet-shooting

Song
Clojure
1
star
37

it-aint-necessarily-so

Music, statistical learning and types.
CSS
1
star
38

dhis2-dev

A vagrant setup to install DHIS2 for development purposes. Not for production use.
Puppet
1
star
39

lambda-jam

Exercises for Lambda Jam workshop
Clojure
1
star
40

whiteboard

A Clojure Scribble client
Clojure
1
star
41

tin-ear

10th anniversary of joining ThoughtWorks performance
Clojure
1
star