epiphany w/ gtk4 and webkitgtk 2.38
[jackhill/guix/guix.git] / tests / monads.scm
CommitLineData
b860f382 1;;; GNU Guix --- Functional package management for GNU
9fdc4b6c 2;;; Copyright © 2013, 2014, 2015, 2016, 2022 Ludovic Courtès <ludo@gnu.org>
b860f382
LC
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (test-monads)
c1bc358f 20 #:use-module (guix tests)
b860f382
LC
21 #:use-module (guix store)
22 #:use-module (guix monads)
23 #:use-module (guix derivations)
e87f0591 24 #:use-module (guix packages)
b860f382
LC
25 #:use-module (gnu packages)
26 #:use-module (gnu packages bootstrap)
4231f05b 27 #:use-module ((gnu packages base) #:select (coreutils))
b860f382
LC
28 #:use-module (ice-9 match)
29 #:use-module (rnrs io ports)
30 #:use-module (srfi srfi-1)
31 #:use-module (srfi srfi-26)
32 #:use-module (srfi srfi-64))
33
405a9d4e 34;; Test the (guix monads) module.
b860f382
LC
35
36(define %store
c1bc358f 37 (open-connection-for-tests))
b860f382 38
ef8de985
LC
39;; Globally disable grafts because they can trigger early builds.
40(%graft? #f)
41
b860f382 42(define %monads
81a97734 43 (list %identity-monad %store-monad %state-monad))
b860f382
LC
44
45(define %monad-run
46 (list identity
81a97734
LC
47 (cut run-with-store %store <>)
48 (cut run-with-state <> '())))
49
50(define-syntax-rule (values->list exp)
51 (call-with-values (lambda () exp)
52 list))
b860f382
LC
53
54\f
55(test-begin "monads")
56
aeb7ec5c
LC
57(test-assert "monad?"
58 (and (every monad? %monads)
59 (every (compose procedure? monad-bind) %monads)
60 (every (compose procedure? monad-return) %monads)))
61
b860f382
LC
62;; The 3 "monad laws": <http://www.haskell.org/haskellwiki/Monad_laws>.
63
64(test-assert "left identity"
65 (every (lambda (monad run)
66 (let ((number (random 777)))
67 (with-monad monad
68 (define (f x)
69 (return (* (1+ number) 2)))
70
71 (= (run (>>= (return number) f))
72 (run (f number))))))
73 %monads
74 %monad-run))
75
76(test-assert "right identity"
77 (every (lambda (monad run)
78 (with-monad monad
79 (let ((number (return (random 777))))
80 (= (run (>>= number return))
81 (run number)))))
82 %monads
83 %monad-run))
84
85(test-assert "associativity"
86 (every (lambda (monad run)
87 (with-monad monad
88 (define (f x)
89 (return (+ 1 x)))
90 (define (g x)
91 (return (* 2 x)))
92
93 (let ((number (return (random 777))))
94 (= (run (>>= (>>= number f) g))
95 (run (>>= number (lambda (x) (>>= (f x) g))))))))
96 %monads
97 %monad-run))
98
99(test-assert "lift"
100 (every (lambda (monad run)
dbbc248a
LC
101 (let ((f (lift1 1+ monad))
102 (g (apply lift1 1+ (list monad))))
b860f382
LC
103 (with-monad monad
104 (let ((number (random 777)))
105 (= (run (>>= (return number) f))
dbbc248a 106 (run (>>= (return number) g))
b860f382
LC
107 (1+ number))))))
108 %monads
109 %monad-run))
110
751630c9
LC
111(test-assert ">>= with more than two arguments"
112 (every (lambda (monad run)
113 (let ((1+ (lift1 1+ monad))
114 (2* (lift1 (cut * 2 <>) monad)))
115 (with-monad monad
116 (let ((number (random 777)))
117 (= (run (>>= (return number)
118 1+ 1+ 1+
119 2* 2* 2*))
120 (* 8 (+ number 3)))))))
121 %monads
122 %monad-run))
123
405a9d4e
LC
124(test-assert "mbegin"
125 (every (lambda (monad run)
126 (with-monad monad
127 (let* ((been-there? #f)
128 (number (mbegin monad
129 (return 1)
130 (begin
131 (set! been-there? #t)
132 (return 2))
133 (return 3))))
134 (and (= (run number) 3)
135 been-there?))))
136 %monads
137 %monad-run))
138
9fdc4b6c
LC
139(test-assert "mparameterize"
140 (let ((parameter (make-parameter 'outside)))
141 (every (lambda (monad run)
142 (equal?
143 (run (mlet monad ((outer (return (parameter)))
144 (inner
145 (mparameterize monad ((parameter 'inside))
146 (return (parameter)))))
147 (return (list outer inner (parameter)))))
148 '(outside inside outside)))
149 %monads
150 %monad-run)))
151
b860f382
LC
152(test-assert "mlet* + text-file + package-file"
153 (run-with-store %store
154 (mlet* %store-monad ((guile (package-file %bootstrap-guile "bin/guile"))
155 (file (text-file "monadic" guile)))
156 (return (equal? (call-with-input-file file get-string-all)
157 guile)))
158 #:guile-for-build (package-derivation %store %bootstrap-guile)))
159
c90ddc8f
LC
160(test-assert "package-file, default system"
161 ;; The default system should be the one at '>>=' time, not the one at
162 ;; invocation time. See <http://bugs.gnu.org/18002>.
163 (run-with-store %store
164 (mlet* %store-monad
165 ((system -> (%current-system))
166 (file (parameterize ((%current-system "foobar64-linux"))
167 (package-file coreutils "bin/ls")))
168 (cu (package->derivation coreutils)))
169 (return (string=? file
170 (string-append (derivation->output-path cu)
171 "/bin/ls"))))
172 #:guile-for-build (package-derivation %store %bootstrap-guile)))
173
4231f05b
LC
174(test-assert "package-file + package->cross-derivation"
175 (run-with-store %store
b4469d8c
LC
176 (mlet* %store-monad ((target -> "mips64el-linux-gnu")
177 (file (package-file coreutils "bin/ls"
178 #:target target))
179 (xcu (package->cross-derivation coreutils target)))
4231f05b
LC
180 (let ((output (derivation->output-path xcu)))
181 (return (string=? file (string-append output "/bin/ls")))))
182 #:guile-for-build (package-derivation %store %bootstrap-guile)))
183
0a90af15
LC
184(test-assert "interned-file"
185 (run-with-store %store
186 (mlet* %store-monad ((file -> (search-path %load-path "guix.scm"))
187 (a (interned-file file))
188 (b (interned-file file "b")))
189 (return (equal? (call-with-input-file file get-string-all)
190 (call-with-input-file a get-string-all)
191 (call-with-input-file b get-string-all))))
192 #:guile-for-build (package-derivation %store %bootstrap-guile)))
193
b860f382
LC
194(test-assert "mapm"
195 (every (lambda (monad run)
196 (with-monad monad
b734996f 197 (equal? (run (mapm monad (lift1 1+ monad) (iota 10)))
b860f382
LC
198 (map 1+ (iota 10)))))
199 %monads
200 %monad-run))
201
202(test-assert "sequence"
203 (every (lambda (monad run)
204 (let* ((input (iota 100))
205 (order '()))
206 (define (frob i)
f62435e2
LC
207 (mlet monad ((foo (return 'foo)))
208 ;; The side effect here is used to keep track of the order in
209 ;; which monadic values are bound. Perform the side effect
210 ;; within a '>>=' so that it is performed when the return
211 ;; value is actually bound.
212 (set! order (cons i order))
213 (return i)))
b860f382
LC
214
215 (and (equal? input
f62435e2 216 (run (sequence monad (map frob input))))
b860f382
LC
217
218 ;; Make sure this is from left to right.
219 (equal? order (reverse input)))))
220 %monads
221 %monad-run))
222
223(test-assert "listm"
224 (every (lambda (monad run)
225 (run (with-monad monad
226 (let ((lst (listm monad
227 (return 1) (return 2) (return 3))))
228 (mlet monad ((lst lst))
229 (return (equal? '(1 2 3) lst)))))))
230 %monads
231 %monad-run))
232
593c3fe6
LC
233(test-assert "anym"
234 (every (lambda (monad run)
235 (eq? (run (with-monad monad
b734996f
LC
236 (anym monad
237 (lift1 (lambda (x)
238 (and (odd? x) 'odd!))
239 monad)
240 (append (make-list 1000 0)
241 (list 1 2)))))
593c3fe6
LC
242 'odd!))
243 %monads
244 %monad-run))
245
81a97734
LC
246(test-equal "set-current-state"
247 (list '(a a d) 'd)
248 (values->list
249 (run-with-state
250 (mlet* %state-monad ((init (current-state))
251 (init2 (set-current-state 'b)))
252 (mbegin %state-monad
253 (set-current-state 'c)
254 (set-current-state 'd)
255 (mlet %state-monad ((last (current-state)))
256 (return (list init init2 last)))))
257 'a)))
258
259(test-equal "state-push etc."
260 (list '((z . 2) (p . (1)) (a . (1))) '(2 1))
261 (values->list
262 (run-with-state
263 (mbegin %state-monad
264 (state-push 1) ;(1)
265 (state-push 2) ;(2 1)
266 (mlet* %state-monad ((z (state-pop)) ;(1)
267 (p (current-state))
268 (a (state-push z))) ;(2 1)
269 (return `((z . ,z) (p . ,p) (a . ,a)))))
270 '())))
271
b860f382 272(test-end "monads")