Mercurial > lasercutter
view 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 source
1 ;; Monadic I/O3 ;; by Konrad Hinsen4 ;; last updated June 24, 20096 ;; Copyright (c) Konrad Hinsen, 2009. All rights reserved. The use7 ;; and distribution terms for this software are covered by the Eclipse8 ;; 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 this10 ;; distribution. By using this software in any fashion, you are11 ;; agreeing to be bound by the terms of this license. You must not12 ;; remove this notice, or any other, from this software.14 (ns15 ^{:author "Konrad Hinsen"16 :doc "Monadic I/O with Java input/output streams17 Defines monadic I/O statements to be used in a state monad18 with an input or output stream as the state. The macro19 monadic-io creates a stream, runs a monadic I/O statement20 on it, and closes the stream. This structure permits the21 definition of purely functional compound I/O statements22 which are applied to streams that can never escape from the23 monadic statement sequence."}24 clojure.contrib.monadic-io-streams25 (:refer-clojure :exclude (read-line print println flush))26 (:use [clojure.contrib.monads27 :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)]))31 ;32 ; Wrap the state into a closure to make sure that "evil" code33 ; 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))]39 ;40 ; Basic stream I/O statements as provided by Java41 ;42 (defn read-char43 "Read a single character"44 []45 (fn [s] [(.read (unlock s)) s]))47 (defn read-line48 "Read a single line"49 []50 (fn [s] [(.readLine (unlock s)) s]))52 (defn skip-chars53 "Skip n characters"54 [n]55 (fn [s] [(.skip (unlock s) n) s]))57 (defn write58 "Write text (a string)"59 [^String text]60 (fn [s] [(.write (unlock s) text) s]))62 (defn flush63 "Flush"64 []65 (fn [s] [(.flush (unlock s)) s]))67 (defn print68 "Print obj"69 [obj]70 (fn [s] [(.print (unlock s) obj) s]))72 (defn println73 "Print obj followed by a newline"74 ([]75 (fn [s] [(.println (unlock s)) s]))76 ([obj]77 (fn [s] [(.println (unlock s) obj) s])))79 ;80 ; Inject I/O streams into monadic I/O statements81 ;82 (defn with-reader83 "Create a reader from reader-spec, run the monadic I/O statement84 on it, and close the reader. reader-spec can be any object accepted85 by clojure.contrib.io/reader."86 [reader-spec statement]87 (with-open [r (reader reader-spec)]88 (first (statement (lock r)))))90 (defn with-writer91 "Create a writer from writer-spec, run the monadic I/O statement92 on it, and close the writer. writer-spec can be any object accepted93 by clojure.contrib.io/writer."94 [writer-spec statement]95 (with-open [w (writer writer-spec)]96 (first (statement (lock w)))))98 (defn with-io-streams99 "Open one or more streams as specified by io-spec, run a monadic100 I/O statement on them, and close the streams. io-spec is101 a binding-like vector in which each stream is specified by102 three element: a keyword by which the stream can be referred to,103 the stream mode (:read or :write), and a stream specification as104 accepted by clojure.contrib.io/reader (mode :read) or105 clojure.contrib.io/writer (mode :write). The statement106 is run on a state which is a map from keywords to corresponding107 streams. Single-stream monadic I/O statements must be wrapped108 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-specs114 opener (cond (= mode :read) reader115 (= mode :write) writer116 :else (throw117 (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))))123 ;124 ; Compound I/O statements125 ;126 (with-monad state-m128 (defn- add-line129 "Read one line and add it to the end of the vector lines. Return130 [lines eof], where eof is an end-of-file flag. The input eof argument131 is not used."132 [[lines eof]]133 (domonad134 [line (read-line)]135 (if (nil? line)136 [lines true]137 [(conj lines line) false])))139 (defn read-lines140 "Read all lines and return them in a vector"141 []142 (domonad143 [[lines eof] (state-m-until second add-line [[] false])]144 lines)))