gnu: python-tempora: Switch to pyproject-build-system.
[jackhill/guix/guix.git] / guix / combinators.scm
CommitLineData
958dd3ce 1;;; GNU Guix --- Functional package management for GNU
ddf9345d 2;;; Copyright © 2012-2017, 2021 Ludovic Courtès <ludo@gnu.org>
958dd3ce 3;;; Copyright © 2014 Eric Bavier <bavier@member.fsf.org>
7a99c58c 4;;; Copyright © 2020 Arun Isaac <arunisaac@systemreboot.net>
958dd3ce
LC
5;;;
6;;; This file is part of GNU Guix.
7;;;
8;;; GNU Guix is free software; you can redistribute it and/or modify it
9;;; under the terms of the GNU General Public License as published by
10;;; the Free Software Foundation; either version 3 of the License, or (at
11;;; your option) any later version.
12;;;
13;;; GNU Guix is distributed in the hope that it will be useful, but
14;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;;; GNU General Public License for more details.
17;;;
18;;; You should have received a copy of the GNU General Public License
19;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21(define-module (guix combinators)
22 #:use-module (ice-9 match)
23 #:use-module (ice-9 vlist)
f9704f17 24 #:export (fold2
958dd3ce
LC
25 fold-tree
26 fold-tree-leaves
ddf9345d
LC
27 compile-time-value
28 procedure-call-location
29 define-compile-time-procedure))
958dd3ce
LC
30
31;;; Commentary:
32;;;
33;;; This module provides useful combinators that complement SRFI-1 and
34;;; friends.
35;;;
36;;; Code:
37
958dd3ce
LC
38(define fold2
39 (case-lambda
40 ((proc seed1 seed2 lst)
41 "Like `fold', but with a single list and two seeds."
42 (let loop ((result1 seed1)
43 (result2 seed2)
44 (lst lst))
45 (if (null? lst)
46 (values result1 result2)
47 (call-with-values
48 (lambda () (proc (car lst) result1 result2))
49 (lambda (result1 result2)
50 (loop result1 result2 (cdr lst)))))))
51 ((proc seed1 seed2 lst1 lst2)
15c29a8a 52 "Like `fold', but with two lists and two seeds."
958dd3ce
LC
53 (let loop ((result1 seed1)
54 (result2 seed2)
55 (lst1 lst1)
56 (lst2 lst2))
57 (if (or (null? lst1) (null? lst2))
58 (values result1 result2)
59 (call-with-values
60 (lambda () (proc (car lst1) (car lst2) result1 result2))
61 (lambda (result1 result2)
7a99c58c 62 (loop result1 result2 (cdr lst1) (cdr lst2)))))))))
958dd3ce
LC
63
64(define (fold-tree proc init children roots)
65 "Call (PROC NODE RESULT) for each node in the tree that is reachable from
66ROOTS, using INIT as the initial value of RESULT. The order in which nodes
67are traversed is not specified, however, each node is visited only once, based
68on an eq? check. Children of a node to be visited are generated by
69calling (CHILDREN NODE), the result of which should be a list of nodes that
70are connected to NODE in the tree, or '() or #f if NODE is a leaf node."
71 (let loop ((result init)
72 (seen vlist-null)
73 (lst roots))
74 (match lst
75 (() result)
76 ((head . tail)
77 (if (not (vhash-assq head seen))
78 (loop (proc head result)
79 (vhash-consq head #t seen)
80 (match (children head)
81 ((or () #f) tail)
82 (children (append tail children))))
83 (loop result seen tail))))))
84
85(define (fold-tree-leaves proc init children roots)
86 "Like fold-tree, but call (PROC NODE RESULT) only for leaf nodes."
87 (fold-tree
88 (lambda (node result)
89 (match (children node)
90 ((or () #f) (proc node result))
91 (else result)))
92 init children roots))
93
94(define-syntax compile-time-value ;not quite at home
95 (syntax-rules ()
96 "Evaluate the given expression at compile time. The expression must
97evaluate to a simple datum."
98 ((_ exp)
99 (let-syntax ((v (lambda (s)
100 (let ((val exp))
101 (syntax-case s ()
102 (_ #`'#,(datum->syntax s val)))))))
103 v))))
104
ddf9345d
LC
105(define-syntax-parameter procedure-call-location
106 (lambda (s)
107 (syntax-violation 'procedure-call-location
108 "'procedure-call-location' may only be used \
109within 'define-compile-time-procedure'"
110 s)))
111
112(define-syntax-rule (define-compile-time-procedure (proc (arg pred) ...)
113 body ...)
114 "Define PROC as a macro such that, if every actual argument in a \"call\"
115matches PRED, then BODY is evaluated at macro-expansion time. BODY must
116return a single value in a type that has read syntax--e.g., numbers, strings,
117lists, etc.
118
119BODY can refer to 'procedure-call-location', which is bound to a source
120property alist corresponding to the call site.
121
122This macro is meant to be used primarily for small procedures that validate or
123process its arguments in a way that may be equally well performed at
124macro-expansion time."
125 (define-syntax proc
126 (lambda (s)
127 (define loc
128 #`(identifier-syntax
129 '#,(datum->syntax #'s (syntax-source s))))
130
131 (syntax-case s ()
132 ((_ arg ...)
133 (and (pred (syntax->datum #'arg)) ...)
134 (let ((arg (syntax->datum #'arg)) ...)
135 (syntax-parameterize ((procedure-call-location
136 (identifier-syntax (syntax-source s))))
137 body ...)))
138 ((_ actual (... ...))
139 #`((lambda (arg ...)
140 (syntax-parameterize ((procedure-call-location #,loc))
141 body ...))
142 actual (... ...)))
143 (id
144 (identifier? #'id)
145 #`(lambda (arg ...)
146 (syntax-parameterize ((procedure-call-location #,loc))
147 body ...)))))))
148
958dd3ce 149;;; combinators.scm ends here