Mercurial > lasercutter
diff 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 |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/clojure/contrib/monadic_io_streams.clj Sat Aug 21 06:25:44 2010 -0400 1.3 @@ -0,0 +1,145 @@ 1.4 +;; Monadic I/O 1.5 + 1.6 +;; by Konrad Hinsen 1.7 +;; last updated June 24, 2009 1.8 + 1.9 +;; Copyright (c) Konrad Hinsen, 2009. All rights reserved. The use 1.10 +;; and distribution terms for this software are covered by the Eclipse 1.11 +;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 1.12 +;; which can be found in the file epl-v10.html at the root of this 1.13 +;; distribution. By using this software in any fashion, you are 1.14 +;; agreeing to be bound by the terms of this license. You must not 1.15 +;; remove this notice, or any other, from this software. 1.16 + 1.17 +(ns 1.18 + ^{:author "Konrad Hinsen" 1.19 + :doc "Monadic I/O with Java input/output streams 1.20 + Defines monadic I/O statements to be used in a state monad 1.21 + with an input or output stream as the state. The macro 1.22 + monadic-io creates a stream, runs a monadic I/O statement 1.23 + on it, and closes the stream. This structure permits the 1.24 + definition of purely functional compound I/O statements 1.25 + which are applied to streams that can never escape from the 1.26 + monadic statement sequence."} 1.27 + clojure.contrib.monadic-io-streams 1.28 + (:refer-clojure :exclude (read-line print println flush)) 1.29 + (:use [clojure.contrib.monads 1.30 + :only (with-monad domonad state-m state-m-until)]) 1.31 + (:use [clojure.contrib.generic.functor :only (fmap)]) 1.32 + (:use [clojure.java.io :only (reader writer)])) 1.33 + 1.34 +; 1.35 +; Wrap the state into a closure to make sure that "evil" code 1.36 +; can't obtain the stream using fetch-state and manipulate it. 1.37 +; 1.38 +(let [key (Object.) 1.39 + lock (fn [state] (fn [x] (if (identical? x key) state nil))) 1.40 + unlock (fn [state] (state key))] 1.41 + 1.42 + ; 1.43 + ; Basic stream I/O statements as provided by Java 1.44 + ; 1.45 + (defn read-char 1.46 + "Read a single character" 1.47 + [] 1.48 + (fn [s] [(.read (unlock s)) s])) 1.49 + 1.50 + (defn read-line 1.51 + "Read a single line" 1.52 + [] 1.53 + (fn [s] [(.readLine (unlock s)) s])) 1.54 + 1.55 + (defn skip-chars 1.56 + "Skip n characters" 1.57 + [n] 1.58 + (fn [s] [(.skip (unlock s) n) s])) 1.59 + 1.60 + (defn write 1.61 + "Write text (a string)" 1.62 + [^String text] 1.63 + (fn [s] [(.write (unlock s) text) s])) 1.64 + 1.65 + (defn flush 1.66 + "Flush" 1.67 + [] 1.68 + (fn [s] [(.flush (unlock s)) s])) 1.69 + 1.70 + (defn print 1.71 + "Print obj" 1.72 + [obj] 1.73 + (fn [s] [(.print (unlock s) obj) s])) 1.74 + 1.75 + (defn println 1.76 + "Print obj followed by a newline" 1.77 + ([] 1.78 + (fn [s] [(.println (unlock s)) s])) 1.79 + ([obj] 1.80 + (fn [s] [(.println (unlock s) obj) s]))) 1.81 + 1.82 + ; 1.83 + ; Inject I/O streams into monadic I/O statements 1.84 + ; 1.85 + (defn with-reader 1.86 + "Create a reader from reader-spec, run the monadic I/O statement 1.87 + on it, and close the reader. reader-spec can be any object accepted 1.88 + by clojure.contrib.io/reader." 1.89 + [reader-spec statement] 1.90 + (with-open [r (reader reader-spec)] 1.91 + (first (statement (lock r))))) 1.92 + 1.93 + (defn with-writer 1.94 + "Create a writer from writer-spec, run the monadic I/O statement 1.95 + on it, and close the writer. writer-spec can be any object accepted 1.96 + by clojure.contrib.io/writer." 1.97 + [writer-spec statement] 1.98 + (with-open [w (writer writer-spec)] 1.99 + (first (statement (lock w))))) 1.100 + 1.101 + (defn with-io-streams 1.102 + "Open one or more streams as specified by io-spec, run a monadic 1.103 + I/O statement on them, and close the streams. io-spec is 1.104 + a binding-like vector in which each stream is specified by 1.105 + three element: a keyword by which the stream can be referred to, 1.106 + the stream mode (:read or :write), and a stream specification as 1.107 + accepted by clojure.contrib.io/reader (mode :read) or 1.108 + clojure.contrib.io/writer (mode :write). The statement 1.109 + is run on a state which is a map from keywords to corresponding 1.110 + streams. Single-stream monadic I/O statements must be wrapped 1.111 + with clojure.contrib.monads/with-state-field." 1.112 + [io-specs statement] 1.113 + (letfn [(run-io [io-specs state statement] 1.114 + (if (zero? (count io-specs)) 1.115 + (first (statement state)) 1.116 + (let [[[key mode stream-spec] & r] io-specs 1.117 + opener (cond (= mode :read) reader 1.118 + (= mode :write) writer 1.119 + :else (throw 1.120 + (Exception. 1.121 + "Mode must be :read or :write")))] 1.122 + (with-open [stream (opener stream-spec)] 1.123 + (run-io r (assoc state key (lock stream)) statement)))))] 1.124 + (run-io (partition 3 io-specs) {} statement)))) 1.125 + 1.126 +; 1.127 +; Compound I/O statements 1.128 +; 1.129 +(with-monad state-m 1.130 + 1.131 + (defn- add-line 1.132 + "Read one line and add it to the end of the vector lines. Return 1.133 + [lines eof], where eof is an end-of-file flag. The input eof argument 1.134 + is not used." 1.135 + [[lines eof]] 1.136 + (domonad 1.137 + [line (read-line)] 1.138 + (if (nil? line) 1.139 + [lines true] 1.140 + [(conj lines line) false]))) 1.141 + 1.142 + (defn read-lines 1.143 + "Read all lines and return them in a vector" 1.144 + [] 1.145 + (domonad 1.146 + [[lines eof] (state-m-until second add-line [[] false])] 1.147 + lines))) 1.148 +