You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

458 lines
15 KiB

  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
  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. (define-module (guix monads)
  19. #:use-module (guix store)
  20. #:use-module (guix derivations)
  21. #:use-module (guix packages)
  22. #:use-module ((system syntax)
  23. #:select (syntax-local-binding))
  24. #:use-module (ice-9 match)
  25. #:use-module (srfi srfi-1)
  26. #:use-module (srfi srfi-9)
  27. #:use-module (srfi srfi-26)
  28. #:export (;; Monads.
  29. define-monad
  30. monad?
  31. monad-bind
  32. monad-return
  33. ;; Syntax.
  34. >>=
  35. return
  36. with-monad
  37. mlet
  38. mlet*
  39. lift1 lift2 lift3 lift4 lift5 lift6 lift7 lift
  40. listm
  41. foldm
  42. mapm
  43. sequence
  44. anym
  45. ;; Concrete monads.
  46. %identity-monad
  47. %store-monad
  48. store-bind
  49. store-return
  50. store-lift
  51. run-with-store
  52. text-file
  53. text-file*
  54. interned-file
  55. package-file
  56. origin->derivation
  57. package->derivation
  58. package->cross-derivation
  59. built-derivations)
  60. #:replace (imported-modules
  61. compiled-modules))
  62. ;;; Commentary:
  63. ;;;
  64. ;;; This module implements the general mechanism of monads, and provides in
  65. ;;; particular an instance of the "store" monad. The API was inspired by that
  66. ;;; of Racket's "better-monads" module (see
  67. ;;; <http://planet.racket-lang.org/package-source/toups/functional.plt/1/1/planet-docs/better-monads-guide/index.html>).
  68. ;;; The implementation and use case were influenced by Oleg Kysielov's
  69. ;;; "Monadic Programming in Scheme" (see
  70. ;;; <http://okmij.org/ftp/Scheme/monad-in-Scheme.html>).
  71. ;;;
  72. ;;; The store monad allows us to (1) build sequences of operations in the
  73. ;;; store, and (2) make the store an implicit part of the execution context,
  74. ;;; rather than a parameter of every single function.
  75. ;;;
  76. ;;; Code:
  77. ;; Record type for monads manipulated at run time.
  78. (define-record-type <monad>
  79. (make-monad bind return)
  80. monad?
  81. (bind monad-bind)
  82. (return monad-return)) ; TODO: Add 'plus' and 'zero'
  83. (define-syntax define-monad
  84. (lambda (s)
  85. "Define the monad under NAME, with the given bind and return methods."
  86. (define prefix (string->symbol "% "))
  87. (define (make-rtd-name name)
  88. (datum->syntax name
  89. (symbol-append prefix (syntax->datum name) '-rtd)))
  90. (syntax-case s (bind return)
  91. ((_ name (bind b) (return r))
  92. (with-syntax ((rtd (make-rtd-name #'name)))
  93. #`(begin
  94. (define rtd
  95. ;; The record type, for use at run time.
  96. (make-monad b r))
  97. (define-syntax name
  98. ;; An "inlined record", for use at expansion time. The goal is
  99. ;; to allow 'bind' and 'return' to be resolved at expansion
  100. ;; time, in the common case where the monad is accessed
  101. ;; directly as NAME.
  102. (lambda (s)
  103. (syntax-case s (%bind %return)
  104. ((_ %bind) #'b)
  105. ((_ %return) #'r)
  106. (_ #'rtd))))))))))
  107. (define-syntax-parameter >>=
  108. ;; The name 'bind' is already taken, so we choose this (obscure) symbol.
  109. (lambda (s)
  110. (syntax-violation '>>= ">>= (bind) used outside of 'with-monad'" s)))
  111. (define-syntax-parameter return
  112. (lambda (s)
  113. (syntax-violation 'return "return used outside of 'with-monad'" s)))
  114. (define-syntax with-monad
  115. (lambda (s)
  116. "Evaluate BODY in the context of MONAD, and return its result."
  117. (syntax-case s ()
  118. ((_ monad body ...)
  119. (eq? 'macro (syntax-local-binding #'monad))
  120. ;; MONAD is a syntax transformer, so we can obtain the bind and return
  121. ;; methods by directly querying it.
  122. #'(syntax-parameterize ((>>= (identifier-syntax (monad %bind)))
  123. (return (identifier-syntax (monad %return))))
  124. body ...))
  125. ((_ monad body ...)
  126. ;; MONAD refers to the <monad> record that represents the monad at run
  127. ;; time, so use the slow method.
  128. #'(syntax-parameterize ((>>= (identifier-syntax
  129. (monad-bind monad)))
  130. (return (identifier-syntax
  131. (monad-return monad))))
  132. body ...)))))
  133. (define-syntax mlet*
  134. (syntax-rules (->)
  135. "Bind the given monadic values MVAL to the given variables VAR. When the
  136. form is (VAR -> VAL), bind VAR to the non-monadic value VAL in the same way as
  137. 'let'."
  138. ;; Note: the '->' symbol corresponds to 'is:' in 'better-monads.rkt'.
  139. ((_ monad () body ...)
  140. (with-monad monad body ...))
  141. ((_ monad ((var mval) rest ...) body ...)
  142. (with-monad monad
  143. (>>= mval
  144. (lambda (var)
  145. (mlet* monad (rest ...)
  146. body ...)))))
  147. ((_ monad ((var -> val) rest ...) body ...)
  148. (let ((var val))
  149. (mlet* monad (rest ...)
  150. body ...)))))
  151. (define-syntax mlet
  152. (lambda (s)
  153. (syntax-case s ()
  154. ((_ monad ((var mval ...) ...) body ...)
  155. (with-syntax (((temp ...) (generate-temporaries #'(var ...))))
  156. #'(mlet* monad ((temp mval ...) ...)
  157. (let ((var temp) ...)
  158. body ...)))))))
  159. (define-syntax define-lift
  160. (syntax-rules ()
  161. ((_ liftn (args ...))
  162. (define (liftn proc monad)
  163. "Lift PROC to MONAD---i.e., return a monadic function in MONAD."
  164. (lambda (args ...)
  165. (with-monad monad
  166. (return (proc args ...))))))))
  167. (define-lift lift1 (a))
  168. (define-lift lift2 (a b))
  169. (define-lift lift3 (a b c))
  170. (define-lift lift4 (a b c d))
  171. (define-lift lift5 (a b c d e))
  172. (define-lift lift6 (a b c d e f))
  173. (define-lift lift7 (a b c d e f g))
  174. (define (lift nargs proc monad)
  175. "Lift PROC, a procedure that accepts NARGS arguments, to MONAD---i.e.,
  176. return a monadic function in MONAD."
  177. (lambda args
  178. (with-monad monad
  179. (return (apply proc args)))))
  180. (define (foldm monad mproc init lst)
  181. "Fold MPROC over LST, a list of monadic values in MONAD, and return a
  182. monadic value seeded by INIT."
  183. (with-monad monad
  184. (let loop ((lst lst)
  185. (result init))
  186. (match lst
  187. (()
  188. (return result))
  189. ((head tail ...)
  190. (mlet* monad ((item head)
  191. (result (mproc item result)))
  192. (loop tail result)))))))
  193. (define (mapm monad mproc lst)
  194. "Map MPROC over LST, a list of monadic values in MONAD, and return a monadic
  195. list. LST items are bound from left to right, so effects in MONAD are known
  196. to happen in that order."
  197. (mlet monad ((result (foldm monad
  198. (lambda (item result)
  199. (mlet monad ((item (mproc item)))
  200. (return (cons item result))))
  201. '()
  202. lst)))
  203. (return (reverse result))))
  204. (define-inlinable (sequence monad lst)
  205. "Turn the list of monadic values LST into a monadic list of values, by
  206. evaluating each item of LST in sequence."
  207. (with-monad monad
  208. (mapm monad return lst)))
  209. (define (anym monad proc lst)
  210. "Apply PROC to the list of monadic values LST; return the first value,
  211. lifted in MONAD, for which PROC returns true."
  212. (with-monad monad
  213. (let loop ((lst lst))
  214. (match lst
  215. (()
  216. (return #f))
  217. ((head tail ...)
  218. (mlet* monad ((value head)
  219. (result -> (proc value)))
  220. (if result
  221. (return result)
  222. (loop tail))))))))
  223. (define-syntax listm
  224. (lambda (s)
  225. "Return a monadic list in MONAD from the monadic values MVAL."
  226. (syntax-case s ()
  227. ((_ monad mval ...)
  228. (with-syntax (((val ...) (generate-temporaries #'(mval ...))))
  229. #'(mlet monad ((val mval) ...)
  230. (return (list val ...))))))))
  231. ;;;
  232. ;;; Identity monad.
  233. ;;;
  234. (define-inlinable (identity-return value)
  235. value)
  236. (define-inlinable (identity-bind mvalue mproc)
  237. (mproc mvalue))
  238. (define-monad %identity-monad
  239. (bind identity-bind)
  240. (return identity-return))
  241. ;;;
  242. ;;; Store monad.
  243. ;;;
  244. ;; return:: a -> StoreM a
  245. (define-inlinable (store-return value)
  246. "Return VALUE from a monadic function."
  247. ;; The monadic value is just this.
  248. (lambda (store)
  249. value))
  250. ;; >>=:: StoreM a -> (a -> StoreM b) -> StoreM b
  251. (define-inlinable (store-bind mvalue mproc)
  252. "Bind MVALUE in MPROC."
  253. (lambda (store)
  254. (let* ((value (mvalue store))
  255. (mresult (mproc value)))
  256. (mresult store))))
  257. (define-monad %store-monad
  258. (bind store-bind)
  259. (return store-return))
  260. (define (store-lift proc)
  261. "Lift PROC, a procedure whose first argument is a connection to the store,
  262. in the store monad."
  263. (define result
  264. (lambda args
  265. (lambda (store)
  266. (apply proc store args))))
  267. (set-object-property! result 'documentation
  268. (procedure-property proc 'documentation))
  269. result)
  270. ;;;
  271. ;;; Store monad operators.
  272. ;;;
  273. (define* (text-file name text)
  274. "Return as a monadic value the absolute file name in the store of the file
  275. containing TEXT, a string."
  276. (lambda (store)
  277. (add-text-to-store store name text '())))
  278. (define* (text-file* name #:rest text)
  279. "Return as a monadic value a derivation that builds a text file containing
  280. all of TEXT. TEXT may list, in addition to strings, packages, derivations,
  281. and store file names; the resulting store file holds references to all these."
  282. (define inputs
  283. ;; Transform packages and derivations from TEXT into a valid input list.
  284. (filter-map (match-lambda
  285. ((? package? p) `("x" ,p))
  286. ((? derivation? d) `("x" ,d))
  287. ((x ...) `("x" ,@x))
  288. ((? string? s)
  289. (and (direct-store-path? s) `("x" ,s)))
  290. (x x))
  291. text))
  292. (define (computed-text text inputs)
  293. ;; Using the lowered INPUTS, return TEXT with derivations replaced with
  294. ;; their output file name.
  295. (define (real-string? s)
  296. (and (string? s) (not (direct-store-path? s))))
  297. (let loop ((inputs inputs)
  298. (text text)
  299. (result '()))
  300. (match text
  301. (()
  302. (string-concatenate-reverse result))
  303. (((? real-string? head) rest ...)
  304. (loop inputs rest (cons head result)))
  305. ((_ rest ...)
  306. (match inputs
  307. (((_ (? derivation? drv) sub-drv ...) inputs ...)
  308. (loop inputs rest
  309. (cons (apply derivation->output-path drv
  310. sub-drv)
  311. result)))
  312. (((_ file) inputs ...)
  313. ;; FILE is the result of 'add-text-to-store' or so.
  314. (loop inputs rest (cons file result))))))))
  315. (define (builder inputs)
  316. `(call-with-output-file (assoc-ref %outputs "out")
  317. (lambda (port)
  318. (display ,(computed-text text inputs) port))))
  319. ;; TODO: Rewrite using 'gexp->derivation'.
  320. (mlet %store-monad ((inputs (lower-inputs inputs)))
  321. (derivation-expression name (builder inputs)
  322. #:inputs inputs)))
  323. (define* (interned-file file #:optional name
  324. #:key (recursive? #t))
  325. "Return the name of FILE once interned in the store. Use NAME as its store
  326. name, or the basename of FILE if NAME is omitted.
  327. When RECURSIVE? is true, the contents of FILE are added recursively; if FILE
  328. designates a flat file and RECURSIVE? is true, its contents are added, and its
  329. permission bits are kept."
  330. (lambda (store)
  331. (add-to-store store (or name (basename file))
  332. recursive? "sha256" file)))
  333. (define* (package-file package
  334. #:optional file
  335. #:key
  336. system (output "out") target)
  337. "Return as a monadic value the absolute file name of FILE within the
  338. OUTPUT directory of PACKAGE. When FILE is omitted, return the name of the
  339. OUTPUT directory of PACKAGE. When TARGET is true, use it as a
  340. cross-compilation target triplet."
  341. (lambda (store)
  342. (define compute-derivation
  343. (if target
  344. (cut package-cross-derivation <> <> target <>)
  345. package-derivation))
  346. (let* ((system (or system (%current-system)))
  347. (drv (compute-derivation store package system))
  348. (out (derivation->output-path drv output)))
  349. (if file
  350. (string-append out "/" file)
  351. out))))
  352. (define (lower-inputs inputs)
  353. "Turn any package from INPUTS into a derivation; return the corresponding
  354. input list as a monadic value."
  355. ;; XXX: This procedure is bound to disappear with 'derivation-expression'.
  356. (with-monad %store-monad
  357. (sequence %store-monad
  358. (map (match-lambda
  359. ((name (? package? package) sub-drv ...)
  360. (mlet %store-monad ((drv (package->derivation package)))
  361. (return `(,name ,drv ,@sub-drv))))
  362. ((name (? string? file))
  363. (return `(,name ,file)))
  364. (tuple
  365. (return tuple)))
  366. inputs))))
  367. (define derivation-expression
  368. ;; XXX: This procedure is superseded by 'gexp->derivation'.
  369. (store-lift build-expression->derivation))
  370. (define package->derivation
  371. (store-lift package-derivation))
  372. (define package->cross-derivation
  373. (store-lift package-cross-derivation))
  374. (define origin->derivation
  375. (store-lift package-source-derivation))
  376. (define imported-modules
  377. (store-lift (@ (guix derivations) imported-modules)))
  378. (define compiled-modules
  379. (store-lift (@ (guix derivations) compiled-modules)))
  380. (define built-derivations
  381. (store-lift build-derivations))
  382. (define* (run-with-store store mval
  383. #:key
  384. (guile-for-build (%guile-for-build))
  385. (system (%current-system)))
  386. "Run MVAL, a monadic value in the store monad, in STORE, an open store
  387. connection."
  388. (define (default-guile)
  389. ;; Lazily resolve 'guile-final'. This module must not refer to (gnu …)
  390. ;; modules directly, to avoid circular dependencies, hence this hack.
  391. (module-ref (resolve-interface '(gnu packages commencement))
  392. 'guile-final))
  393. (parameterize ((%guile-for-build (or guile-for-build
  394. (package-derivation store
  395. (default-guile)
  396. system)))
  397. (%current-system system))
  398. (mval store)))
  399. ;;; monads.scm end here