Mercurial > lasercutter
comparison src/clojure/contrib/monadic_io_streams.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 ;; Monadic I/O | |
2 | |
3 ;; by Konrad Hinsen | |
4 ;; last updated June 24, 2009 | |
5 | |
6 ;; Copyright (c) Konrad Hinsen, 2009. All rights reserved. The use | |
7 ;; and distribution terms for this software are covered by the Eclipse | |
8 ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) | |
9 ;; which can be found in the file epl-v10.html at the root of this | |
10 ;; distribution. By using this software in any fashion, you are | |
11 ;; agreeing to be bound by the terms of this license. You must not | |
12 ;; remove this notice, or any other, from this software. | |
13 | |
14 (ns | |
15 ^{:author "Konrad Hinsen" | |
16 :doc "Monadic I/O with Java input/output streams | |
17 Defines monadic I/O statements to be used in a state monad | |
18 with an input or output stream as the state. The macro | |
19 monadic-io creates a stream, runs a monadic I/O statement | |
20 on it, and closes the stream. This structure permits the | |
21 definition of purely functional compound I/O statements | |
22 which are applied to streams that can never escape from the | |
23 monadic statement sequence."} | |
24 clojure.contrib.monadic-io-streams | |
25 (:refer-clojure :exclude (read-line print println flush)) | |
26 (:use [clojure.contrib.monads | |
27 :only (with-monad domonad state-m state-m-until)]) | |
28 (:use [clojure.contrib.generic.functor :only (fmap)]) | |
29 (:use [clojure.java.io :only (reader writer)])) | |
30 | |
31 ; | |
32 ; Wrap the state into a closure to make sure that "evil" code | |
33 ; can't obtain the stream using fetch-state and manipulate it. | |
34 ; | |
35 (let [key (Object.) | |
36 lock (fn [state] (fn [x] (if (identical? x key) state nil))) | |
37 unlock (fn [state] (state key))] | |
38 | |
39 ; | |
40 ; Basic stream I/O statements as provided by Java | |
41 ; | |
42 (defn read-char | |
43 "Read a single character" | |
44 [] | |
45 (fn [s] [(.read (unlock s)) s])) | |
46 | |
47 (defn read-line | |
48 "Read a single line" | |
49 [] | |
50 (fn [s] [(.readLine (unlock s)) s])) | |
51 | |
52 (defn skip-chars | |
53 "Skip n characters" | |
54 [n] | |
55 (fn [s] [(.skip (unlock s) n) s])) | |
56 | |
57 (defn write | |
58 "Write text (a string)" | |
59 [^String text] | |
60 (fn [s] [(.write (unlock s) text) s])) | |
61 | |
62 (defn flush | |
63 "Flush" | |
64 [] | |
65 (fn [s] [(.flush (unlock s)) s])) | |
66 | |
67 (defn print | |
68 "Print obj" | |
69 [obj] | |
70 (fn [s] [(.print (unlock s) obj) s])) | |
71 | |
72 (defn println | |
73 "Print obj followed by a newline" | |
74 ([] | |
75 (fn [s] [(.println (unlock s)) s])) | |
76 ([obj] | |
77 (fn [s] [(.println (unlock s) obj) s]))) | |
78 | |
79 ; | |
80 ; Inject I/O streams into monadic I/O statements | |
81 ; | |
82 (defn with-reader | |
83 "Create a reader from reader-spec, run the monadic I/O statement | |
84 on it, and close the reader. reader-spec can be any object accepted | |
85 by clojure.contrib.io/reader." | |
86 [reader-spec statement] | |
87 (with-open [r (reader reader-spec)] | |
88 (first (statement (lock r))))) | |
89 | |
90 (defn with-writer | |
91 "Create a writer from writer-spec, run the monadic I/O statement | |
92 on it, and close the writer. writer-spec can be any object accepted | |
93 by clojure.contrib.io/writer." | |
94 [writer-spec statement] | |
95 (with-open [w (writer writer-spec)] | |
96 (first (statement (lock w))))) | |
97 | |
98 (defn with-io-streams | |
99 "Open one or more streams as specified by io-spec, run a monadic | |
100 I/O statement on them, and close the streams. io-spec is | |
101 a binding-like vector in which each stream is specified by | |
102 three element: a keyword by which the stream can be referred to, | |
103 the stream mode (:read or :write), and a stream specification as | |
104 accepted by clojure.contrib.io/reader (mode :read) or | |
105 clojure.contrib.io/writer (mode :write). The statement | |
106 is run on a state which is a map from keywords to corresponding | |
107 streams. Single-stream monadic I/O statements must be wrapped | |
108 with clojure.contrib.monads/with-state-field." | |
109 [io-specs statement] | |
110 (letfn [(run-io [io-specs state statement] | |
111 (if (zero? (count io-specs)) | |
112 (first (statement state)) | |
113 (let [[[key mode stream-spec] & r] io-specs | |
114 opener (cond (= mode :read) reader | |
115 (= mode :write) writer | |
116 :else (throw | |
117 (Exception. | |
118 "Mode must be :read or :write")))] | |
119 (with-open [stream (opener stream-spec)] | |
120 (run-io r (assoc state key (lock stream)) statement)))))] | |
121 (run-io (partition 3 io-specs) {} statement)))) | |
122 | |
123 ; | |
124 ; Compound I/O statements | |
125 ; | |
126 (with-monad state-m | |
127 | |
128 (defn- add-line | |
129 "Read one line and add it to the end of the vector lines. Return | |
130 [lines eof], where eof is an end-of-file flag. The input eof argument | |
131 is not used." | |
132 [[lines eof]] | |
133 (domonad | |
134 [line (read-line)] | |
135 (if (nil? line) | |
136 [lines true] | |
137 [(conj lines line) false]))) | |
138 | |
139 (defn read-lines | |
140 "Read all lines and return them in a vector" | |
141 [] | |
142 (domonad | |
143 [[lines eof] (state-m-until second add-line [[] false])] | |
144 lines))) | |
145 |