combinators.scm 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2012-2017, 2021 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2014 Eric Bavier <bavier@member.fsf.org>
  4. ;;; Copyright © 2020 Arun Isaac <arunisaac@systemreboot.net>
  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. (define-module (guix combinators)
  21. #:use-module (ice-9 match)
  22. #:use-module (ice-9 vlist)
  23. #:export (fold2
  24. fold-tree
  25. fold-tree-leaves
  26. compile-time-value
  27. procedure-call-location
  28. define-compile-time-procedure))
  29. ;;; Commentary:
  30. ;;;
  31. ;;; This module provides useful combinators that complement SRFI-1 and
  32. ;;; friends.
  33. ;;;
  34. ;;; Code:
  35. (define fold2
  36. (case-lambda
  37. ((proc seed1 seed2 lst)
  38. "Like `fold', but with a single list and two seeds."
  39. (let loop ((result1 seed1)
  40. (result2 seed2)
  41. (lst lst))
  42. (if (null? lst)
  43. (values result1 result2)
  44. (call-with-values
  45. (lambda () (proc (car lst) result1 result2))
  46. (lambda (result1 result2)
  47. (loop result1 result2 (cdr lst)))))))
  48. ((proc seed1 seed2 lst1 lst2)
  49. "Like `fold', but with two lists and two seeds."
  50. (let loop ((result1 seed1)
  51. (result2 seed2)
  52. (lst1 lst1)
  53. (lst2 lst2))
  54. (if (or (null? lst1) (null? lst2))
  55. (values result1 result2)
  56. (call-with-values
  57. (lambda () (proc (car lst1) (car lst2) result1 result2))
  58. (lambda (result1 result2)
  59. (loop result1 result2 (cdr lst1) (cdr lst2)))))))))
  60. (define (fold-tree proc init children roots)
  61. "Call (PROC NODE RESULT) for each node in the tree that is reachable from
  62. ROOTS, using INIT as the initial value of RESULT. The order in which nodes
  63. are traversed is not specified, however, each node is visited only once, based
  64. on an eq? check. Children of a node to be visited are generated by
  65. calling (CHILDREN NODE), the result of which should be a list of nodes that
  66. are connected to NODE in the tree, or '() or #f if NODE is a leaf node."
  67. (let loop ((result init)
  68. (seen vlist-null)
  69. (lst roots))
  70. (match lst
  71. (() result)
  72. ((head . tail)
  73. (if (not (vhash-assq head seen))
  74. (loop (proc head result)
  75. (vhash-consq head #t seen)
  76. (match (children head)
  77. ((or () #f) tail)
  78. (children (append tail children))))
  79. (loop result seen tail))))))
  80. (define (fold-tree-leaves proc init children roots)
  81. "Like fold-tree, but call (PROC NODE RESULT) only for leaf nodes."
  82. (fold-tree
  83. (lambda (node result)
  84. (match (children node)
  85. ((or () #f) (proc node result))
  86. (else result)))
  87. init children roots))
  88. (define-syntax compile-time-value ;not quite at home
  89. (syntax-rules ()
  90. "Evaluate the given expression at compile time. The expression must
  91. evaluate to a simple datum."
  92. ((_ exp)
  93. (let-syntax ((v (lambda (s)
  94. (let ((val exp))
  95. (syntax-case s ()
  96. (_ #`'#,(datum->syntax s val)))))))
  97. v))))
  98. (define-syntax-parameter procedure-call-location
  99. (lambda (s)
  100. (syntax-violation 'procedure-call-location
  101. "'procedure-call-location' may only be used \
  102. within 'define-compile-time-procedure'"
  103. s)))
  104. (define-syntax-rule (define-compile-time-procedure (proc (arg pred) ...)
  105. body ...)
  106. "Define PROC as a macro such that, if every actual argument in a \"call\"
  107. matches PRED, then BODY is evaluated at macro-expansion time. BODY must
  108. return a single value in a type that has read syntax--e.g., numbers, strings,
  109. lists, etc.
  110. BODY can refer to 'procedure-call-location', which is bound to a source
  111. property alist corresponding to the call site.
  112. This macro is meant to be used primarily for small procedures that validate or
  113. process its arguments in a way that may be equally well performed at
  114. macro-expansion time."
  115. (define-syntax proc
  116. (lambda (s)
  117. (define loc
  118. #`(identifier-syntax
  119. '#,(datum->syntax #'s (syntax-source s))))
  120. (syntax-case s ()
  121. ((_ arg ...)
  122. (and (pred (syntax->datum #'arg)) ...)
  123. (let ((arg (syntax->datum #'arg)) ...)
  124. (syntax-parameterize ((procedure-call-location
  125. (identifier-syntax (syntax-source s))))
  126. body ...)))
  127. ((_ actual (... ...))
  128. #`((lambda (arg ...)
  129. (syntax-parameterize ((procedure-call-location #,loc))
  130. body ...))
  131. actual (... ...)))
  132. (id
  133. (identifier? #'id)
  134. #`(lambda (arg ...)
  135. (syntax-parameterize ((procedure-call-location #,loc))
  136. body ...)))))))
  137. ;;; combinators.scm ends here