lens.texi 9.76 KB
Newer Older
Alex Sassmannshausen's avatar
Alex Sassmannshausen committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

\input texinfo
@c -*-texinfo-*-

@c %**start of header
@setfilename guile-lens.info
@documentencoding UTF-8
@settitle Guile Lens Reference Manual
@c %**end of header

@include version.texi

@copying
Copyright @copyright{} 2018 Alex Sassmannshausen

Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A
copy of the license is included in the section entitled ``GNU Free
Documentation License''.
@end copying

@dircategory The Algorithmic Language Scheme
@direntry
* Guile Lens: (guile-lens)    
@end direntry

@titlepage
@title The Guile Lens Manual
@author Alex Sassmannshausen

@page
@vskip 0pt plus 1filll
Edition @value{EDITION} @*
@value{UPDATED} @*

@insertcopying
@end titlepage

@contents

@c *********************************************************************
@node Top
@top Guile Lens

This document describes Guile Lens version @value{VERSION}.

@menu
* Introduction::                Why Guile Lens?
Alex Sassmannshausen's avatar
Alex Sassmannshausen committed
51 52 53 54 55 56 57
* Lenses::                      Creating & composing lenses
* Using them::                  Focus, put & over
* Batteries included::          Lenses that come with the library
* Procedure index::             An index of all exposed procedures

@c Add index of existing lenses

Alex Sassmannshausen's avatar
Alex Sassmannshausen committed
58 59 60 61 62 63
@end menu

@c *********************************************************************
@node Introduction
@chapter Introduction

Alex Sassmannshausen's avatar
Alex Sassmannshausen committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
Guile Lens is a Christmas project.  It is a port of
@uref{http://funcool.github.io/lentes/latest/#introduction, Clojure's
Lentes library}.  Lenses are a way to @emph{zoom in} on parts of
arbitrarily complex data structures.

For a nice introduction of lenses in relation to JavaScript, see
@uref{https://medium.com/javascript-scene/lenses-b85976cb0534, Eric
Elliott's Lenses article}.

Lenses are procedures that combine getters and setters into one
structure.  You can then use the @code{focus}, @code{put} or
@code{over} procedures together with a lens to either get, set or
perform an arbitrary transformation on part of the datastructure that
the lens was designed for.

Because lenses are composable you can use them to manipulate parts of
deeply nested datastructures in an immutable fashion.

In short, @emph{lenses} allow you to focus on @emph{parts} of a
@emph{whole}.

@c *********************************************************************
@node Lenses
@chapter Lenses

@defun lens getter setter
@code{lens} is the building block of the library.  You can use it to
construct new, compoosable lenses.  Composability of lenses means that
you can use the @code{compose} procedure to combine multiple lenses in
the same way that you would combine procedures.  The resulting lens
can then be used by the @ref{focus}, @ref{put} or @ref{over} commands
to perform operations on the structure that is the object of the
composed lens.

This composition hence allows you to spelunk forward, ever more deeply
into datastructures: the first lens might take you to the third
element of a list; the second lens might then take you to the first
field in a record; finally the third lens might take you to the 5th
word of the selected passphrase.

@var{getter} should be a procedure of one argument, the object we are
exploring.  The procedure should return the part of the object we'd
like the lens to focus on.

@var{setter} is a procedure of 2 arguments, the object we are
exploring and a procedure to be applied to the element we'd like the
lens to focus on.  This @var{setter} procedure should return a new
version of the original object, where the focused part is replaced by
the value resulting from applying the passed procedure to that part.

@lisp
;; This is an example implementation.  The library comes with a lens
;; called `snd', which does the below, and which is implemented on the
;; basis of the `nth' lens.  `nth' is a general implementation of a
;; lens on lists.

(define (second-lens)
  "Return a lens focusing on the second element of a list."
  (lens second
        (lambda (list procedure)
          (cons* (first list) (procedure (second list))
                 (cddr list)))))
@end lisp
@end defun

@c *********************************************************************
@node Using them
@chapter Using them

This library implements several procedures that make working with
lenses convenient.  These procedures are @code{focus}, @code{put} and
@code{over}.

@anchor{focus}
@defun focus lens object
@code{focus} allows you to extract @emph{part} of the @var{object} by
applying @var{lens} to it.  It simply returns the value that the lens
is concerned with.  In the simplest cases, this is the same as
applying a field-accessor to a record.
@end defun

@anchor{put}
@defun put lens value object
@code{put} allows you to replace the @emph{part} of the @var{object}
that @var{lens} focuses on with @var{value}.  It returns a new version
of @var{object}, and can thus be chained.  In the simplest cases, this
is the same as applying a field-setter to an immutable record.
@end defun

@anchor{over}
@defun over lens procedure object
@code{over} allows you to apply an arbitrary @var{procedure} to
@emph{part} to that part of the @var{object} that the @var{lens}
focuses on.  It returns a new version of @var{object}, and can thus be
chained.  In the simplest cases, this is the same as applying a
field-setter after applying @var{procedure} to the field-getter of an
immutable record.
@end defun

@c *********************************************************************
@node Batteries included
@chapter Batteries included

This library provides you with the tools you need to create your own
lenses.  It does however also come with a bunch of more-or-less useful
predefined lenses.  Below is a comprehensive list.

@defun id
@code{id} is the identity lens.  It focuses on the whole object.
@lisp
(focus (id) '(a b c))
;; => '(a b c)

(put (id) '(d e f) '(a b c))
;; => '(d e f)
@end lisp
@end defun

@defun nth N
@code{nth} is a generalised list lens.  You can use it to focus on the
@var{N}th element of a list.
@lisp
(focus (nth 2) '(a b c))
;; => c

(put (nth 2) '(d e f) '(a b c))
;; => '(a b (d e f))
@end lisp
@end defun

@defun fst
@code{fst} is a specialised list lens.  You can use it to focus on the
first element of a list.
@lisp
(focus (fst) '(a b c))
;; => a

(put (fst) '(d e f) '(a b c))
;; => '((d e f) b c)
@end lisp
@end defun

@defun snd
@code{snd} is a specialised list lens.  You can use it to focus on the
second element of a list.
@lisp
(focus (snd) '(a b c))
;; => b

(put (snd) '(d e f) '(a b c))
;; => '(a (d e f) c)
@end lisp
@end defun

@defun tail
@code{tail} is a list lens that focuses on the tail of lists.
@lisp
(focus (tail) '(a b c))
;; => '(b c)

(put (tail) '(d e f) '(a b c))
;; => '(a d e f)
@end lisp
@end defun

@defun key K
@code{key} is a lens over association lists.  With it you can focus on
the entry in the association list identified by @var{K}.
@lisp
(focus (key 'b) '((a . hello) (b . beautiful) (c . world)))
;; => '(b . beautiful)

(put (key 'b) '(d . placid) '((a . hello) (b . beautiful) (c . world)))
;; => '((a . hello) (d . placid) (c . world))
@end lisp
@end defun

@defun key-ref K
@code{key-ref} is a lens over association lists.  With it you can
focus on the value of the entry in the association list identified by
@var{K}.
@lisp
(focus (key-ref 'b) '((a . hello) (b . beautiful) (c . world)))
;; => 'beautiful

(put (key-ref 'b) 'placid '((a . hello) (b . beautiful) (c . world)))
;; => '((a . hello) (b . placid) (c . world))
@end lisp
@end defun

@defun select-keys . Ks
@code{select-keys} is a lens over association lists.  With it you can
focus on the entries in the association list identified by @var{Ks}.

Be aware that using @code{put} with @code{select-keys}
will result in all selected entries being replaced with the value
being passed in.  You probably want to use @code{over}.
@lisp
(focus (select-keys 'b 'c) '((a . hello) (b . beautiful) (c . world)))
;; => '((b . beautiful) (c . world))

(put (key-ref 'b) 'placid '((a . hello) (b . beautiful) (c . world)))
;; => '((d . placid) (d . placid) (a . hello))
@end lisp
@end defun

@defun in . path
@code{in} can be used with a nested association list.  @var{path}
specfies the set of keys to travers the nested list with.
@lisp
(focus (in 'b 'bb)
       '((a . hello) (b . ((aa . test) (bb . beautiful))) (c . world)))
;; => 'beautiful

(put (in 'b 'bb) 'placid
     '((a . hello) (b . ((aa . test) (bb . beautiful))) (c . world)))
;; => '((a . hello) (b (aa . test) (bb . placid)) (c . world))
@end lisp
@end defun

@defun passes predicate
@code{passes} is the @code{id} lens only when @var{predicate} passes.
You can use this lens to focus on a @emph{whole} only when it meets
certain criteria.
@lisp
(focus (passes (compose (cut eq? <> 'a) first)) '(a b c))
;; => '(a b c)

(focus (passes (compose (cut eq? <> 'b) first)) '(a b c))
;; => #<unspecified>

(put (passes (compose (cut eq? <> 'a) first)) '(d e f) '(a b c))
;; => '(d e f)

(put (passes (compose (cut eq? <> 'b) first)) '(d e f) '(a b c))
;; => '(a b c)
@end lisp
@end defun

@defun passes* predicate true-lens false-lens
@code{passes*} is a more general version of @code{passes}.  You can
use it to dispatch to multiple lenses by testing on @var{predicate}.
If @var{predicate} evaluates to #f, use @var{false-lens}. Otherwise,
use @var{true-lens}.
@lisp
(focus (passes* (compose list? first) (fst) (id)) '(a b c))
;; => '(a b c)

(focus (passes* (compose list? first) (fst) (id)) '((a) b c))
;; => '(a)

(put (passes* (compose list? first) (fst) (id)) '(d e f) '(a b c))
;; => '(d e f)

(put (passes* (compose list? first) (fst) (id)) '(d e f) '((a) b c))
;; => '(a b c)
@end lisp
@end defun

@c *********************************************************************
@node Procedure index
@unnumbered Procedure index
Alex Sassmannshausen's avatar
Alex Sassmannshausen committed
326

Alex Sassmannshausen's avatar
Alex Sassmannshausen committed
327
@printindex fn
Alex Sassmannshausen's avatar
Alex Sassmannshausen committed
328 329

@bye