Commit 14b15d07 authored by Alex Sassmannshausen's avatar Alex Sassmannshausen

Change file structure because of bug in hall.

* lens/core.scm: New file.
* lens/lenses.scm: New file.
* lens.scm: Use above 2 & re-export.
* hall.scm: Update to reflect file structure.
parent 3db0d226
......@@ -4,14 +4,19 @@
(version "0.1")
(author "Alex Sassmannshausen")
(copyright '(2018))
(synopsis "Composable lenses for data structures in Guile")
(description "Guile-Lens is a library implementing lenses in Guile. The library is currently a re-implementation of the lentes library for Clojure. Lenses provide composable procedures, which can be used to focus, apply functions over, or update a value in arbitrary data structures.")
(synopsis
"Composable lenses for data structures in Guile")
(description
"Guile-Lens is a library implementing lenses in Guile. The library is currently a re-implementation of the lentes library for Clojure. Lenses provide composable procedures, which can be used to focus, apply functions over, or update a value in arbitrary data structures.")
(home-page
"https://gitlab.com/a-sassmannshausen/")
(license gpl3+)
(dependencies `())
(files (libraries
((scheme-file "lens") (directory "lens" ())))
((scheme-file "lens")
(directory
"lens"
((scheme-file "lenses") (scheme-file "core")))))
(tests ((directory "tests" ((scheme-file "lens")))))
(programs ((directory "scripts" ())))
(documentation
......
......@@ -28,185 +28,12 @@
;;; Code:
(define-module (lens)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-9)
#:use-module (srfi srfi-9 gnu)
#:use-module (srfi srfi-26)
#:export (lens
focus over put
#:use-module (lens core)
#:use-module (lens lenses)
#:re-export (lens
focus over put
id nth fst snd tail key key-ref select-keys in in* passes passes*
id nth fst snd tail key key-ref select-keys in in* passes
passes*
units))
(define (lens getter setter)
"Return a composable lens, which can be used to focus, apply functions over,
or update a value in an arbitrary data structure.
GETTER is a procedure of one argument, the data structure, that can be used to
access the focused item in the data structure.
SETTER is a procedure of two arguments, the data structure & a procedure to be
applied to the data structure to transform the focused value."
(lambda (previous)
(match-lambda*
((s)
(getter (previous s)))
((s f) (previous s (cut setter <> f))))))
(define (id-setter s f)
"The identity setter. IDENTITY is the identity getter."
(f s))
(define (focus lens s)
"Focus on a value in the data structure S by using the lens LENS."
(let ((getter (lens identity)))
(getter s)))
(define (over lens f s)
"Apply the function F over the value focused on by the lens LENS in the data
structure S."
(let ((setter (lens id-setter)))
(setter s f)))
(define (put lens v s)
"Replace the value focused on by the lens LENS with the new value V in data
structure S."
(over lens (const v) s))
;;;; Lenses
(define (id)
"Return the identity lens."
(lens identity id-setter))
(define (nth n)
"Given a number N, return a lens that will focus on the N-th element of a
list."
(lens (cut list-ref <> n)
(lambda (s f)
(reverse
(second
(fold (match-lambda*
((current ((? (cut = <> n) counter) result))
(list (1+ counter) (cons (f current) result)))
((current (counter result))
(list (1+ counter) (cons current result))))
'(0 ())
s))))))
(define (fst)
"Return a lens that will focus on the first element of a list."
(nth 0))
(define (snd)
"Return a lens that will focus on the second element of a list."
(nth 1))
(define (tail)
"Return a lens that focuses on the tail of a list."
(lens cdr
(lambda (s f)
(match s
(() (f '()))
((1st . rest) (cons 1st (f rest)))))))
(define (key k)
"Given K, return a lens that will focus on the pair in an association list
who's key is K."
(lens (cut assoc k <>)
(lambda (s f)
(map (match-lambda
(((? (cut equal? k <>)) . v) (f v))
(entry entry))
s))))
;; NOTE: KEY-REF only differs from KEY in getter, not setter. This works
;; apparently (see IN, below), but might be cause of strangeness?
(define (key-ref k)
"Given K, return a lens that will focus on the value of the pair in an
association list who's key is K."
(lens (cut assoc-ref <> k)
(lambda (s f)
(map (match-lambda
(((? (cut equal? k <>)) . v) (cons k (f v)))
(entry entry))
s))))
(define (select-keys . ks)
"Given a list of keys KS, return a lens that will select only those pairs in
an association list whose keys are in KS."
(lens (lambda (s) (map (cut assoc <> s) ks))
(lambda (s f)
;; PUT currently does not update only values of selected keys, but
;; replaces each selected key with the entire set of values
(append (filter-map (lambda (k) (and=> (assoc k s) f)) ks)
(remove (compose (cut member <> ks) first) s)))))
(define assoc?
(match-lambda
(((a . b) ...) #t)
(_ #f)))
;; FIXME: This implementation is closer to source material, but doesn't have a
;; setter yet.
(define in*
(match-lambda*
((path)
(in* path '()))
((path default)
(lens
(lambda (s)
(let lp ((next (first path))
(rest (cdr path))
(target s))
(match (assoc next target)
(#f default)
((k . v)
(match rest
(() `(,k . ,v))
((next . rst)
(lp next
rst
(if (assoc? v)
v
(throw 'in-lens 'atom-reached: v
'path-not-done: rest)))))))))
(lambda (s f)
(f s))))))
;; FIXME: current inconsistency: in focus, throw if unknown path; in put/over,
;; simply return original assoc-list
(define (in . path)
"Given the breadcrumb trail PATH, return a lens that will traverse a nested
association list and focus on the final element in PATH."
(apply compose (reverse (map (cut key-ref <>) path))))
;;;;; Conditional lenses
;; NOTE: This works nicely as the leaf in the composition of lenses, but does
;; not provide us with conditional lensing. See passes* for the latter use
;; case.
(define (passes applies?)
"Given a predicate, return a lens that focuses on an element only if it
passes the predicate."
(lens
(lambda (s) (when (applies? s) s))
(lambda (s f) (if (applies? s) (f s) s))))
(define (passes* applies? true false)
"Given a predicate, a consequent lens TRUE and an alternative lens FALSE,
return a lens that lenses an element with the consequent if it passes the
predicate, or the alternative if it fails."
(lens
(lambda (s) (if (applies? s) (focus true s) (focus false s)))
(lambda (s f) (if (applies? s) (over true f s) (over false f s)))))
;;;; Combinators
(define (units one->other other->one)
"Given a function from unit A to unit B and another in the opposite
direction, construct a lens that focuses and updates a converted value."
(lens one->other
(lambda (s f) (other->one (f (one->other s))))))
units))
;; lens/core.scm --- core implementation -*- coding: utf-8 -*-
;;
;; Copyright (C) 2019 Alex Sassmannshausen <[email protected]>
;;
;; Author: Alex Sassmannshausen <[email protected]>
;;
;; This file is part of guile-lens.
;;
;; guile-lens is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published by the Free
;; Software Foundation; either version 3 of the License, or (at your option)
;; any later version.
;;
;; guile-lens is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
;; for more details.
;;
;; You should have received a copy of the GNU General Public License along
;; with guile-lens; if not, contact:
;;
;; Free Software Foundation Voice: +1-617-542-5942
;; 59 Temple Place - Suite 330 Fax: +1-617-542-2652
;; Boston, MA 02111-1307, USA [email protected]
;;; Commentary:
;;
;;; Code:
(define-module (lens core)
#:use-module (ice-9 match)
#:use-module (srfi srfi-26)
#:export (lens focus over put id-setter))
(define (lens getter setter)
"Return a composable lens, which can be used to focus, apply functions over,
or update a value in an arbitrary data structure.
GETTER is a procedure of one argument, the data structure, that can be used to
access the focused item in the data structure.
SETTER is a procedure of two arguments, the data structure & a procedure to be
applied to the data structure to transform the focused value."
(lambda (previous)
(match-lambda*
((s)
(getter (previous s)))
((s f) (previous s (cut setter <> f))))))
(define (id-setter s f)
"The identity setter. IDENTITY is the identity getter."
(f s))
(define (focus lens s)
"Focus on a value in the data structure S by using the lens LENS."
(let ((getter (lens identity)))
(getter s)))
(define (over lens f s)
"Apply the function F over the value focused on by the lens LENS in the data
structure S."
(let ((setter (lens id-setter)))
(setter s f)))
(define (put lens v s)
"Replace the value focused on by the lens LENS with the new value V in data
structure S."
(over lens (const v) s))
;; lens/lenses.scm --- lenses implementation -*- coding: utf-8 -*-
;;
;; Copyright (C) 2019 Alex Sassmannshausen <[email protected]>
;;
;; Author: Alex Sassmannshausen <[email protected]>
;;
;; This file is part of guile-lens.
;;
;; guile-lens is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published by the Free
;; Software Foundation; either version 3 of the License, or (at your option)
;; any later version.
;;
;; guile-lens is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
;; for more details.
;;
;; You should have received a copy of the GNU General Public License along
;; with guile-lens; if not, contact:
;;
;; Free Software Foundation Voice: +1-617-542-5942
;; 59 Temple Place - Suite 330 Fax: +1-617-542-2652
;; Boston, MA 02111-1307, USA [email protected]
;;; Commentary:
;;
;;; Code:
(define-module (lens lenses)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
#:use-module (lens core)
#:export (id
nth fst snd tail key key-ref select-keys in in* passes passes*
units))
;;;; Lenses
(define (id)
"Return the identity lens."
(lens identity id-setter))
(define (nth n)
"Given a number N, return a lens that will focus on the N-th element of a
list."
(lens (cut list-ref <> n)
(lambda (s f)
(reverse
(second
(fold (match-lambda*
((current ((? (cut = <> n) counter) result))
(list (1+ counter) (cons (f current) result)))
((current (counter result))
(list (1+ counter) (cons current result))))
'(0 ())
s))))))
(define (fst)
"Return a lens that will focus on the first element of a list."
(nth 0))
(define (snd)
"Return a lens that will focus on the second element of a list."
(nth 1))
(define (tail)
"Return a lens that focuses on the tail of a list."
(lens cdr
(lambda (s f)
(match s
(() (f '()))
((1st . rest) (cons 1st (f rest)))))))
(define (key k)
"Given K, return a lens that will focus on the pair in an association list
who's key is K."
(lens (cut assoc k <>)
(lambda (s f)
(map (match-lambda
(((? (cut equal? k <>)) . v) (f v))
(entry entry))
s))))
;; NOTE: KEY-REF only differs from KEY in getter, not setter. This works
;; apparently (see IN, below), but might be cause of strangeness?
(define (key-ref k)
"Given K, return a lens that will focus on the value of the pair in an
association list who's key is K."
(lens (cut assoc-ref <> k)
(lambda (s f)
(map (match-lambda
(((? (cut equal? k <>)) . v) (cons k (f v)))
(entry entry))
s))))
(define (select-keys . ks)
"Given a list of keys KS, return a lens that will select only those pairs in
an association list whose keys are in KS."
(lens (lambda (s) (map (cut assoc <> s) ks))
(lambda (s f)
;; PUT currently does not update only values of selected keys, but
;; replaces each selected key with the entire set of values
(append (filter-map (lambda (k) (and=> (assoc k s) f)) ks)
(remove (compose (cut member <> ks) first) s)))))
(define assoc?
(match-lambda
(((a . b) ...) #t)
(_ #f)))
;; FIXME: This implementation is closer to source material, but doesn't have a
;; setter yet.
(define in*
(match-lambda*
((path)
(in* path '()))
((path default)
(lens
(lambda (s)
(let lp ((next (first path))
(rest (cdr path))
(target s))
(match (assoc next target)
(#f default)
((k . v)
(match rest
(() `(,k . ,v))
((next . rst)
(lp next
rst
(if (assoc? v)
v
(throw 'in-lens 'atom-reached: v
'path-not-done: rest)))))))))
(lambda (s f)
(f s))))))
;; FIXME: current inconsistency: in focus, throw if unknown path; in put/over,
;; simply return original assoc-list
(define (in . path)
"Given the breadcrumb trail PATH, return a lens that will traverse a nested
association list and focus on the final element in PATH."
(apply compose (reverse (map (cut key-ref <>) path))))
;;;;; Conditional lenses
;; NOTE: This works nicely as the leaf in the composition of lenses, but does
;; not provide us with conditional lensing. See passes* for the latter use
;; case.
(define (passes applies?)
"Given a predicate, return a lens that focuses on an element only if it
passes the predicate."
(lens
(lambda (s) (when (applies? s) s))
(lambda (s f) (if (applies? s) (f s) s))))
(define (passes* applies? true false)
"Given a predicate, a consequent lens TRUE and an alternative lens FALSE,
return a lens that lenses an element with the consequent if it passes the
predicate, or the alternative if it fails."
(lens
(lambda (s) (if (applies? s) (focus true s) (focus false s)))
(lambda (s f) (if (applies? s) (over true f s) (over false f s)))))
;;;; Combinators
(define (units one->other other->one)
"Given a function from unit A to unit B and another in the opposite
direction, construct a lens that focuses and updates a converted value."
(lens one->other
(lambda (s f) (other->one (f (one->other s))))))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment