-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | A collection of Oleg Kiselyov's Haskell modules (2009-2008) -- -- A collection of Oleg Kiselyov's Haskell modules (2009-2008) (released -- with his permission) @package liboleg @version 0.2 -- | http://okmij.org/ftp/Haskell/regions.html#light-weight -- -- Lightweight monadic regions -- --
-- infoM $ printf "%s saw %s with %s" (show x) (show y) (show z) ---- -- Writing show on and on quickly becomes tiresome. It turns out, we can -- avoid these repeating show, still conforming to Haskell98. -- -- Our polyvariadic generic printf is like polyvariadic show with the -- printf-like format string. Our printf handles values of any present -- and future type for which there is a Show instance. For example: -- --
-- t1 = unR $ printf "Hi there" -- -- "Hi there" ---- --
-- t2 = unR $ printf "Hi %s!" "there" -- -- "Hi there!" ---- --
-- t3 = unR $ printf "The value of %s is %s" "x" 3 -- -- "The value of x is 3" ---- --
-- t4 = unR $ printf "The value of %s is %s" "x" [5] -- -- "The value of x is [5]" ---- -- The unsightly unR appears solely for Haskell98 compatibility: flexible -- instances remove the need for it. On the other hand, Evan Klitzke's -- code post-processes the result of formatting with infoM, which can -- subsume unR. -- -- A bigger problem with our generic printf, shared with the original -- Text.Printf.printf, is partiality: The errors like passing too many or -- too few arguments to printf are caught only at run-time. We can -- certainly do better. -- -- Version: The current version is 1.1, June 5, 2009. -- -- References -- --
-- instance E (F x) (F x) -- instance (E y y', A (F x) y' r) => E ((F x) :< y) r -- instance (E (x :< y) r', E (r' :< z) r) => E ((x :< y) :< z) r ---- -- The first line says that abstractions evaluate to themselves, the -- second line executes the redex, and the third recurses to find it. -- Still, we may (and did) write partial applications, the fixpoint -- combinator, Fibonacci function, and S and K combinators. Incidentally, -- the realization of the S combinator again takes three lines, two of -- which build the closures (partial applications) and the third line -- executes the familiar S-rule. -- --
-- instance A (F CombS) f (F (CombS,f)) -- instance A (F (CombS,f)) g (F (CombS,f,g)) -- instance E (f :< x :< (g :< x)) r => A (F (CombS,f,g)) x r ---- -- Incidentally, the present code constitutes what seems to be the -- shortest proof that the type system of Haskell with the undecidable -- instances extension is indeed Turing-complete. That extension is -- needed for the fixpoint combinator -- which is present in the system -- described in Luca Cardelli's 1988 manuscript: -- -- http://lucacardelli.name/Papers/PhaseDistinctions.pdf -- -- As he wrote, ``Here we have generalized the language of constant -- expressions to include typed lambda abstraction, application and -- recursion (because of the latter we do not require compile-time -- computations to terminate).'' [p9] -- -- This message is all the code, which runs in GHC 6.4.1 - 6.8.2 (it -- could well run in Hugs; alas, Hugs did not like infix type -- constructors like :<). module Language.TypeLC -- | First we define some constants data HTrue HTrue :: HTrue data HFalse HFalse :: HFalse data Zero Zero :: Zero data Su a Su :: a -> Su a -- | Indicator for functions, or applicable things: data F x class A l a b | l a -> b -- | The meaning of |A l a b| is that the application to |a| of an -- applicable thing denoted by |l| yields |b|. -- -- Surprisingly, this already works. Let us define the boolean Not -- function data FNot -- | The next function is the boolean And. It takes two arguments, so we -- have to handle currying now. data FAnd -- | Commonly, abstraction is held to be `more fundamental', which is -- reflected in the popular phrase `Lambda-the-Ultimate'. In our system, -- application is fundamental. An abstraction is a not-yet-applied -- application -- which is in itself a first-class value. The class A -- defines the meaning of a function, and an instance of A becomes the -- definition of a function (clause). -- -- We have dealt with simple expressions and applicative things. We now -- build sequences of applications, and define the corresponding big step -- semantics. We introduce the syntax for applications: data (:<) f x -- | and the big-step evaluation function: class E a b | a -> b -- | That is all. The rest of the message are the tests. The first one is -- the type-level equivalent of the following function: -- --
-- testb x = and (not x) (not (not x)) ---- -- At the type level, it looks not much differently: type Testb x = (E ((F FAnd :< (F FNot :< x)) :< (F FNot :< (F FNot :< x))) r) => r -- | We introduce the applicative fixpoint combinator data Rec l -- | and define the sum of two numerals -- -- At the type level, this looks as follows data FSum' -- | now we tie up the knot type FSum = Rec (F FSum') type N0 = Zero type N1 = Su N0 type N2 = Su N1 type N3 = Su N2 test_sum :: (E ((F FSum :< x) :< y) r) => x -> y -> r -- | Finally, the standard test -- Fibonacci data Fib' type Fib = Rec (F Fib') test_fib :: (E (F Fib :< n) r) => n -> r -- | Finally, we demonstrate that our calculus is combinatorially complete, -- by writing the S and K combinators data CombK data CombS -- | A few examples: SKK as the identity type Test_skk x = (E (((F CombS :< F CombK) :< F CombK) :< x) r) => r -- | and the representation of numerals in the SK calculus. The expression -- (F FSum :< Su Zero) is a partial application of the function sum to -- 1. type CombZ = F CombS :< F CombK type CombSu = F CombS :< ((F CombS :< (F CombK :< F CombS)) :< F CombK) type CombTwo = CombSu :< (CombSu :< CombZ) test_ctwo :: (E ((CombTwo :< (F FSum :< Su Zero)) :< Zero) r) => r class Nat a fromNat :: (Nat a) => a -> Integer instance (Nat x) => Show (Su x) instance Show Zero instance (Nat x) => Nat (Su x) instance Nat Zero instance Show HFalse instance Show HTrue instance (E ((f :< x) :< (g :< x)) r) => A (F (CombS, f, g)) x r instance A (F (CombS, f)) g (F (CombS, f, g)) instance A (F CombS) f (F (CombS, f)) instance A (F (CombK, a)) b a instance A (F CombK) a (F (CombK, a)) instance (E ((F FSum :< (self :< n)) :< (self :< Su n)) r) => A (F (Fib', self)) (Su (Su n)) r instance A (F (Fib', self)) (Su Zero) (Su Zero) instance A (F (Fib', self)) Zero (Su Zero) instance A (F Fib') self (F (Fib', self)) instance (E ((self :< n) :< m) r) => A (F (FSum', self, Su n)) m (Su r) instance A (F (FSum', self, Zero)) m m instance A (F (FSum', self)) n (F (FSum', self, n)) instance A (F FSum') self (F (FSum', self)) instance (E ((l :< F (Rec l)) :< x) r) => A (F (Rec l)) x r instance (E x x') => E (Su x) (Su x') instance (E (x :< y) r', E (r' :< z) r) => E ((x :< y) :< z) r instance (E y y', A (F x) y' r) => E (F x :< y) r instance E (F x) (F x) instance E Zero Zero instance E HFalse HFalse instance E HTrue HTrue instance A (F (FAnd, HFalse)) a HFalse instance A (F (FAnd, HTrue)) a a instance A (F FAnd) x (F (FAnd, x)) instance A (F FNot) HFalse HTrue instance A (F FNot) HTrue HFalse -- | http://okmij.org/ftp/Haskell/types.html#computable-types -- -- Part I of the series introduced the type-level functional language -- with the notation that resembles lambda-calculus with case -- distinction, fixpoint recursion, etc. Modulo a bit of syntactic tart, -- the language of the type functions even looks almost like the pure -- Haskell. In this message, we show the applications of computable -- types, to ascribe signatures to terms and to drive the selection of -- overloaded functions. We can compute the type of a tree of the depth -- fib(N) or a complex XML type, and instantiate the read function to -- read the trees of only that shape. -- -- A telling example of the power of the approach is the ability to use -- not only (a->) but also (->a) as an unary type function. The -- former is just (->) a. The latter is considered impossible. In our -- approach, (->a) is written almost literally as (flip (->) a) -- -- This series of messages has been inspired by Luca Cardelli's 1988 -- manuscript ``Phase Distinctions in Type Theory'' -- http://lucacardelli.name/Papers/PhaseDistinctions.pdf and by -- Simon Peyton Jones and Erik Meijer's ``Henk: A Typed Intermediate -- Language'' -- http://www.research.microsoft.com/~simonpj/Papers/henk.ps.gz -- which expounds many of the same notions in an excellent tutorial style -- and in modern terminology. -- -- I'm very grateful to Chung-chieh Shan for pointing out these papers to -- me and for thought-provoking discussions. module Language.TypeFN -- | Our first example comes from Cardelli's paper: defining the type -- Prop(n), of n-ary propositions. That is, -- --
-- Prop(2) should be the type Bool -> Bool -> Bool -- Prop(3) is the type of functions Bool -> Bool -> Bool -> Bool ---- -- etc. -- -- Cardelli's paper (p. 10) writes this type as -- --
-- let Prop:: AllKK(N:: NatK) Type = -- recK(F:: AllKK(N:: NatK) Type) -- funKK(N:: NatK) caseK N of 0K => Bool; succK(M) => Bool -> F(M); ---- --
-- let and2: Prop(2K) = -- fun(a: Bool) fun(b: Bool) a & b; ---- -- Here 0K and 2K are type-level numerals of the kind NatK; recK is the -- type-level fix-point combinator. The computed type Prop(2) then gives -- the type to the term and2. -- -- In our system, this example looks as follows: data Prop' type Prop = Rec (F Prop') type Prop2 = (E (F Prop :< N2) r) => r -- | We can compute types by applying type functions, just as we can -- compute values by applying regular functions. Indeed, let us define a -- StrangeProp of the kind NAT -> Type. StrangeProp(n) is the type of -- propositions of arity m, where m is fib(n). We compose together the -- already defined type function Prop2 and the type function Fib in the -- previous message. data StrangeProp oddand4t :: (E (F StrangeProp :< ((F FSum :< N2) :< N2)) r) => r -- | We introduce the combinator Ntimes: |NTimes f x n| applies f to x n -- times. This is a sort of fold over numerals. data Ntimes data ATC1 c -- | We can then write the type of n-ary propositions Prop(N) in a -- different way, as an N-times application of the type constructor -- (Bool->) to Bool. type PropN' n = (E (((F Ntimes :< (F (ATC1 ((->) Bool)))) :< Bool) :< n) r) => r -- | To generalize, data ATC2 c :: (* -> * -> *) type SPropN' n = (E (((F Ntimes :< (F (ATC2 (->)) :< Bool)) :< Bool) :< (F Fib :< n)) r) => r -- | The comparison of ATC1 with ATC2 shows two different ways of defining -- abstractions: as (F x) terms (`lambda-terms' in our calculus) and as -- polymorphic types (Haskell type abstractions). The two ways are -- profoundly related. The more verbose type application notation, via -- (:<), has its benefits. After we introduce another higher-order -- function data Flip -- | we make a very little change type SSPropN' n = (E (((F Ntimes :< ((F Flip :< (F (ATC2 (->)))) :< Bool)) :< Bool) :< (F Fib :< n)) r) => r -- | and obtain quite a different result: -- --
-- *TypeFN> :t test_sspn4 -- test_sspn4 :: ((((Bool -> Bool) -> Bool) -> Bool) -> Bool) -> Bool ---- -- In effect, we were able to use not only (a->) but also (->a) as -- an unary type function. Moreover, we achieved the latter by almost -- literally applying the flip function to the arrow type constructor -- (->). -- -- Using the type inspection tools (typecast), we can replace the family -- of functions ATC1, ATC2 with one, kind-polymorphic, polyvariadic -- function ATC. This approach will be explained in further messages. -- -- We can use the computed types to drive overloaded functions such as -- read and show. The specifically instantiated read functions, in -- particular, will check that a (remotely) received serialized value -- matches our expectation. Let's consider the type of trees of the depth -- of at most N. data Node v els Node :: v -> [els] -> Node v els type TreeDN v l n = (E (((F Ntimes :< (F (ATC1 (Node v)))) :< l) :< n) r) => r instance (Read v, Read els) => Read (Node v els) instance (Show v, Show els) => Show (Node v els) instance E (Node v els) (Node v els) instance (E ((f :< y) :< x) r) => A (F (Flip, f, x)) y r instance A (F (Flip, f)) x (F (Flip, f, x)) instance A (F Flip) f (F (Flip, f)) instance A (F (ATC2 c)) x (F (ATC1 (c x))) instance A (F (ATC1 c)) x (c x) instance (E (((F Ntimes :< f) :< (f :< x)) :< n) r) => A (F (Ntimes, f, x)) (Su n) r instance A (F (Ntimes, f, x)) Zero x instance A (F (Ntimes, f)) x (F (Ntimes, f, x)) instance A (F Ntimes) f (F (Ntimes, f)) instance E (a, b) (a, b) instance E (a -> b) (a -> b) instance E String String instance E Int Int instance E Bool Bool instance (E (F Prop :< (F Fib :< n)) r) => A (F StrangeProp) n r instance (E (f :< m) r) => A (F (Prop', f)) (Su m) (Bool -> r) instance A (F (Prop', f)) Zero Bool instance A (F Prop') f (F (Prop', f)) -- | Monadic and General Iteratees: incremental input parsers, processors -- and transformers -- -- The running example, parts 1 and 2 Part 1 is reading the headers, the -- sequence of lines terminated by an empty line. Each line is terminated -- by CR, LF, or CRLF. We should return the headers in order. In the case -- of error, we should return the headers read so far and the description -- of the error. Part 2 is reading the headers and reading all the lines -- from the HTTP-chunk-encoded content that follows the headers. Part 2 -- thus verifies layering of streams, and processing of one stream -- embedded (chunk encoded) into another stream. module System.IterateeM -- | A stream is a (continuing) sequence of elements bundled in Chunks. The -- first two variants indicate termination of the stream. Chunk [a] gives -- the currently available part of the stream. The stream is not -- terminated yet. The case (Chunk []) signifies a stream with no -- currently available data but which is still continuing. A stream -- processor should, informally speaking, ``suspend itself'' and wait for -- more data to arrive. Later on, we can add another variant: IE_block -- (Ptr CChar) CSize so we could parse right from the buffer. data StreamG a EOF :: StreamG a Err :: String -> StreamG a Chunk :: [a] -> StreamG a -- | A particular instance of StreamG: the stream of characters. This -- stream is used by many input parsers. type Stream = StreamG Char -- | Iteratee -- a generic stream processor, what is being folded over a -- stream When Iteratee is in the done state, it contains the -- computed result and the remaining part of the stream. In the -- cont state, the iteratee has not finished the computation and -- needs more input. We assume that all iteratees are good -- -- given bounded input, they do the bounded amount of computation and -- take the bounded amount of resources. The monad m describes the sort -- of computations done by the iteratee as it processes the stream. The -- monad m could be the identity monad (for pure computations) or the IO -- monad (to let the iteratee store the stream processing results as they -- are computed). We also assume that given a terminated stream, an -- iteratee moves to the done state, so the results computed so far could -- be returned. -- -- We could have used existentials instead, by doing the closure -- conversion data IterateeG el m a IE_done :: a -> (StreamG el) -> IterateeG el m a IE_cont :: (StreamG el -> IterateeGM el m a) -> IterateeG el m a newtype IterateeGM el m a IM :: m (IterateeG el m a) -> IterateeGM el m a unIM :: IterateeGM el m a -> m (IterateeG el m a) type Iteratee m a = IterateeG Char m a type IterateeM m a = IterateeGM Char m a -- | Useful combinators for implementing iteratees and enumerators liftI :: (Monad m) => IterateeG el m a -> IterateeGM el m a -- | Just like bind (at run-time, this is indeed exactly bind) (>>==) :: (Monad m) => IterateeGM el m a -> (IterateeG el m a -> IterateeGM el' m b) -> IterateeGM el' m b -- | Just like an application -- a call-by-value-like application (==<<) :: (Monad m) => (IterateeG el m a -> IterateeGM el' m b) -> IterateeGM el m a -> IterateeGM el' m b -- | The following is a variant of join in the IterateeGM el m -- monad. When el' is the same as el, the type of joinI is indeed that of -- true monadic join. However, joinI is subtly different: since generally -- el' is different from el, it makes no sense to continue using the -- internal, IterateeG el' m a: we no longer have elements of the type -- el' to feed to that iteratee. We thus send EOF to the internal -- Iteratee and propagate its result. This join function is useful when -- dealing with `derived iteratees' for embedded/nested streams. In -- particular, joinI is useful to process the result of stake, -- map_stream, or conv_stream below. joinI :: (Monad m) => IterateeGM el m (IterateeG el' m a) -> IterateeGM el m a -- | Read a stream to the end and return all of its elements as a list stream2list :: (Monad m) => IterateeGM el m [el] -- | Check to see if the stream is in error iter_report_err :: (Monad m) => IterateeGM el m (Maybe String) -- | The analogue of List.break It takes an element predicate and returns a -- pair: (str, Just c) -- the element c is the first element of -- the stream satisfying the break predicate; The list str is the prefix -- of the stream up to but including c (str,Nothing) -- The -- stream is terminated with EOF or error before any element satisfying -- the break predicate was found. str is the scanned part of the stream. -- None of the element in str satisfy the break predicate. sbreak :: (Monad m) => (el -> Bool) -> IterateeGM el m ([el], Maybe el) -- | A particular optimized case of the above: skip all elements of the -- stream satisfying the given predicate -- until the first element that -- does not satisfy the predicate, or the end of the stream. This is the -- analogue of List.dropWhile sdropWhile :: (Monad m) => (el -> Bool) -> IterateeGM el m () -- | Attempt to read the next element of the stream Return (Just c) if -- successful, return Nothing if the stream is terminated (by EOF or an -- error) snext :: (Monad m) => IterateeGM el m (Maybe el) -- | Look ahead at the next element of the stream, without removing it from -- the stream. Return (Just c) if successful, return Nothing if the -- stream is terminated (by EOF or an error) speek :: (Monad m) => IterateeGM el m (Maybe el) -- | Skip the rest of the stream skip_till_eof :: (Monad m) => IterateeGM el m () -- | Skip n elements of the stream, if there are that many This is the -- analogue of List.drop sdrop :: (Monad m) => Int -> IterateeGM el m () -- | Iteratee converters for stream embedding The converters show a -- different way of composing two iteratees: vertical rather -- than horizontal -- -- The type of the converter from the stream with elements el_outer to -- the stream with element el_inner. The result is the iteratee for the -- outer stream that uses an `IterateeG el_inner m a' to process the -- embedded, inner stream as it reads the outer stream. type EnumeratorN el_outer el_inner m a = IterateeG el_inner m a -> IterateeGM el_outer m (IterateeG el_inner m a) -- | Read n elements from a stream and apply the given iteratee to the -- stream of the read elements. Unless the stream is terminated early, we -- read exactly n elements (even if the iteratee has accepted fewer). stake :: (Monad m) => Int -> EnumeratorN el el m a -- | Map the stream: yet another iteratee transformer Given the stream of -- elements of the type el and the function el->el', build a nested -- stream of elements of the type el' and apply the given iteratee to it. -- Note the contravariance map_stream :: (Monad m) => (el -> el') -> EnumeratorN el el' m a -- | Convert one stream into another, not necessarily in lockstep -- The transformer map_stream maps one element of the outer stream to one -- element of the nested stream. The transformer below is more general: -- it may take several elements of the outer stream to produce one -- element of the inner stream, or the other way around. The -- transformation from one stream to the other is specified as IterateeGM -- el m (Maybe [el']). The Maybe type reflects the possibility of -- the conversion error. conv_stream :: (Monad m) => IterateeGM el m (Maybe [el']) -> EnumeratorN el el' m a -- | Combining the primitive iteratees to solve the running problem: -- Reading headers and the content from an HTTP-like stream type Line = String -- | Read the line of text from the stream The line can be terminated by -- CR, LF or CRLF. Return (Right Line) if successful. Return (Left Line) -- if EOF or a stream error were encountered before the terminator is -- seen. The returned line is the string read so far. -- -- The code is the same as that of pure Iteratee, only the signature has -- changed. Compare the code below with GHCBufferIO.line_lazy line :: (Monad m) => IterateeM m (Either Line Line) -- | Line iteratees: processors of a stream whose elements are made of -- Lines -- -- Collect all read lines and return them as a list see stream2list -- -- Print lines as they are received. This is the first impure -- iteratee with non-trivial actions during chunk processing print_lines :: IterateeGM Line IO () -- | Convert the stream of characters to the stream of lines, and apply the -- given iteratee to enumerate the latter. The stream of lines is -- normally terminated by the empty line. When the stream of characters -- is terminated, the stream of lines is also terminated, abnormally. -- This is the first proper iteratee-enumerator: it is the iteratee of -- the character stream and the enumerator of the line stream. More -- generally, we could have used conv_stream to implement enum_lines. enum_lines :: (Monad m) => EnumeratorN Char Line m a -- | Convert the stream of characters to the stream of words, and apply the -- given iteratee to enumerate the latter. Words are delimited by white -- space. This is the analogue of List.words It is instructive to compare -- the code below with the code of List.words, which is: -- --
-- words :: String -> [String] -- words s = case dropWhile isSpace s of -- "" -> [] -- s' -> w : words s'' -- where (w, s'') = -- break isSpace s' ---- -- One should keep in mind that enum_words is a more general, monadic -- function. More generally, we could have used conv_stream to implement -- enum_words. enum_words :: (Monad m) => EnumeratorN Char String m a -- | Enumerators Each enumerator takes an iteratee and returns an iteratee -- an Enumerator is an iteratee transformer. The enumerator normally -- stops when the stream is terminated or when the iteratee moves to the -- done state, whichever comes first. When to stop is of course up to the -- enumerator... -- -- We have two choices of composition: compose iteratees or compose -- enumerators. The latter is useful when one iteratee reads from the -- concatenation of two data sources. type EnumeratorGM el m a = IterateeG el m a -> IterateeGM el m a type EnumeratorM m a = EnumeratorGM Char m a -- | The most primitive enumerator: applies the iteratee to the terminated -- stream. The result is the iteratee usually in the done state. enum_eof :: (Monad m) => EnumeratorGM el m a -- | Another primitive enumerator: report an error enum_err :: (Monad m) => String -> EnumeratorGM el m a -- | The composition of two enumerators: essentially the functional -- composition It is convenient to flip the order of the arguments of the -- composition though: in e1 >. e2, e1 is executed first (>.) :: (Monad m) => EnumeratorGM el m a -> EnumeratorGM el m a -> EnumeratorGM el m a -- | The pure 1-chunk enumerator It passes a given list of elements to the -- iteratee in one chunk This enumerator does no IO and is useful for -- testing of base parsing enum_pure_1chunk :: (Monad m) => [el] -> EnumeratorGM el m a -- | The pure n-chunk enumerator It passes a given lift of elements to the -- iteratee in n chunks This enumerator does no IO and is useful for -- testing of base parsing and handling of chunk boundaries enum_pure_nchunk :: (Monad m) => [el] -> Int -> EnumeratorGM el m a -- | The enumerator of a POSIX Fd Unlike fdRead (which allocates a new -- buffer on each invocation), we use the same buffer all throughout enum_fd :: Fd -> EnumeratorM IO a enum_file :: FilePath -> EnumeratorM IO a -- | HTTP chunk decoding Each chunk has the following format: -- --
-- <chunk-size> CRLF <chunk-data> CRLF ---- -- where chunk-size is the hexadecimal number; chunk-data -- is a sequence of chunk-size bytes. The last chunk (so-called -- EOF chunk) has the format 0 CRLF CRLF (where 0 is an ASCII zero, a -- character with the decimal code 48). For more detail, see Chunked -- Transfer Coding, Sec 3.6.1 of the HTTP/1.1 standard: -- http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 -- -- The following enum_chunk_decoded has the signature of the enumerator -- of the nested (encapsulated and chunk-encoded) stream. It receives an -- iteratee for the embedded stream and returns the iteratee for the -- base, embedding stream. Thus what is an enumerator and what is an -- iteratee may be a matter of perspective. -- -- We have a decision to make: Suppose an iteratee has finished (either -- because it obtained all needed data or encountered an error that makes -- further processing meaningless). While skipping the rest of the -- stream/the trailer, we encountered a framing error (e.g., missing CRLF -- after chunk data). What do we do? We chose to disregard the latter -- problem. Rationale: when the iteratee has finished, we are in the -- process of skipping up to the EOF (draining the source). Disregarding -- the errors seems OK then. Also, the iteratee may have found an error -- and decided to abort further processing. Flushing the remainder of the -- input is reasonable then. One can make a different choice... enum_chunk_decoded :: (Monad m) => Iteratee m a -> IterateeM m a instance (Show a) => Show (StreamG a) instance MonadTrans (IterateeGM el) instance (Monad m) => Monad (IterateeGM el m) -- | Random and Binary IO with IterateeM -- -- http://okmij.org/ftp/Streams.html#random-bin-IO -- -- Random and binary IO: Reading TIFF -- -- Iteratees presuppose sequential processing. A general-purpose input -- method must also support random IO: processing a seek-able input -- stream from an arbitrary position, jumping back and forth through the -- stream. We demonstrate random IO with iteratees, as well as reading -- non-textual files and converting raw bytes into multi-byte quantities -- such as integers, rationals, and TIFF dictionaries. Positioning of the -- input stream is evocative of delimited continuations. -- -- We use random and binary IO to write a general-purpose TIFF library. -- The library emphasizes incremental processing, relying on iteratees -- and enumerators for on-demand reading of tag values. The library -- extensively uses nested streams, tacitly converting the stream of raw -- bytes from the file into streams of integers, rationals and other -- user-friendly items. The pixel matrix is presented as a contiguous -- stream, regardless of its segmentation into strips and physical -- arrangement. -- -- We show a representative application of the library: reading a sample -- TIFF file, printing selected values from the TIFF dictionary, -- verifying the values of selected pixels and computing the histogram of -- pixel values. The pixel verification procedure stops reading the pixel -- matrix as soon as all specified pixel values are verified. The -- histogram accumulation does read the entire matrix, but incrementally. -- Neither pixel matrix processing procedure loads the whole matrix in -- memory. In fact, we never read and retain more than the IO-buffer-full -- of raw data. -- -- Version: The current version is 1.1, December 2008. module System.RandomIO -- | The type of the IO monad supporting seek requests and endianness The -- seek_request is not-quite a state, more like a `communication channel' -- set by the iteratee and answered by the enumerator. Since the base -- monad is IO, it seems simpler to implement both endianness and seek -- requests as IORef cells. Their names are grouped in a structure -- RBState, which is propagated as the `environment.' newtype RBIO a RBIO :: (RBState -> IO a) -> RBIO a unRBIO :: RBIO a -> RBState -> IO a -- | Generally, RBState is opaque and should not be exported. data RBState RBState :: IORef Bool -> IORef (Maybe FileOffset) -> RBState msb_first :: RBState -> IORef Bool seek_req :: RBState -> IORef (Maybe FileOffset) -- | The programmer should use the following functions instead -- -- To request seeking, the iteratee sets seek_req to (Just -- desired_offset) When the enumerator answers the request, it sets -- seek_req back to Nothing rb_seek_set :: FileOffset -> RBIO () rb_seek_answered :: RBIO Bool rb_msb_first :: RBIO Bool rb_msb_first_set :: Bool -> RBIO () runRB :: RBState -> IterateeGM el RBIO a -> IO (IterateeG el RBIO a) -- | A useful combinator. Perhaps a better idea would have been to define -- Iteratee to have (Maybe a) in IE_done? In that case, we could make -- IterateeGM to be the instance of MonadPlus bindm :: (Monad m) => m (Maybe a) -> (a -> m (Maybe b)) -> m (Maybe b) -- | We discard all available input first. We keep discarding the stream s -- until we determine that our request has been answered: rb_seek_set -- sets the state seek_req to (Just off). When the request is answered, -- the state goes back to Nothing. The above features remind one of -- delimited continuations. sseek :: FileOffset -> IterateeGM el RBIO () -- | An iteratee that reports and propagates an error We disregard the -- input first and then propagate error. It is reminiscent of -- abort iter_err :: (Monad m) => String -> IterateeGM el m () -- | Read n elements from a stream and apply the given iteratee to the -- stream of the read elements. If the given iteratee accepted fewer -- elements, we stop. This is the variation of stake with the -- early termination of processing of the outer stream once the -- processing of the inner stream finished early. This variation is -- particularly useful for randomIO, where we do not have to care to -- `drain the input stream'. stakeR :: (Monad m) => Int -> EnumeratorN el el m a -- | Iteratees to read unsigned integers written in Big- or Little-endian -- ways endian_read2 :: IterateeGM Word8 RBIO (Maybe Word16) endian_read4 :: IterateeGM Word8 RBIO (Maybe Word32) -- | The enumerator of a POSIX Fd: a variation of enum_fd that supports -- RandomIO (seek requests) enum_fd_random :: Fd -> EnumeratorGM Word8 RBIO a instance MonadIO RBIO instance Monad RBIO -- | A general-purpose TIFF library -- -- http://okmij.org/ftp/Streams.html#random-bin-IO -- -- The library gives the user the TIFF dictionary, which the user can -- search for specific tags and obtain the values associated with the -- tags, including the pixel matrix. -- -- The overarching theme is incremental processing: initially, only the -- TIFF dictionary is read. The value associated with a tag is read only -- when that tag is looked up (unless the value was short and was packed -- in the TIFF dictionary entry). The pixel matrix (let alone the whole -- TIFF file) is not loaded in memory -- the pixel matrix is not even -- located before it is needed. The matrix is processed incrementally, by -- a user-supplied iteratee. -- -- The incremental processing is accomplished by iteratees and -- enumerators. The enumerators are indeed first-class, they are stored -- in the interned TIFF dictionary data structure. These enumerators -- represent the values associated with tags; the values will be read on -- demand, when the enumerator is applied to a user-given iteratee. -- -- The library extensively uses nested streams, tacitly converting the -- stream of raw bytes from the file into streams of integers, rationals -- and other user-friendly items. The pixel matrix is presented as a -- contiguous stream, regardless of its segmentation into strips and -- physical arrangement. The library exhibits random IO and binary -- parsing, reading of multi-byte numeric data in big- or little-endian -- formats. The library can be easily adopted for AIFF, RIFF and other -- IFF formats. -- -- We show a representative application of the library: reading a sample -- TIFF file, printing selected values from the TIFF dictionary, -- verifying the values of selected pixels and computing the histogram of -- pixel values. The pixel verification procedure stops reading the pixel -- matrix as soon as all specified pixel values are verified. The -- histogram accumulation does read the entire matrix, but incrementally. -- Neither pixel matrix processing procedure loads the whole matrix in -- memory. In fact, we never read and retain more than the IO-buffer-full -- of raw data. module Codec.Image.Tiff -- | Sample TIFF user code The following is sample code using the TIFF -- library (whose implementation is in the second part of this file). Our -- sample code prints interesting information from the TIFF dictionary -- (such as the dimensions, the resolution and the name of the image) -- -- The sample file is a GNU logo (from http:www.gnu.org) converted -- from JPG to TIFF. Copyleft by GNU. -- -- The main user function. tiff_reader is the library function, which -- builds the TIFF dictionary. process_tiff is the user function, to -- extract useful data from the dictionary -- -- Sample TIFF processing function -- -- sample processing of the pixel matrix: computing the histogram compute_hist :: TIFFDict -> IterateeGM Word8 RBIO (Int, IntMap Int) -- | Another sample processor of the pixel matrix: verifying values of some -- pixels This processor does not read the whole matrix; it stops as soon -- as everything is verified or the error is detected -- -- TIFF library code -- -- We need a more general enumerator type: enumerator that maps streams -- (not necessarily in lock-step). This is a flattened (`joinI-ed') -- EnumeratorN elfrom elto m a type EnumeratorGMM elfrom elto m a = IterateeG elto m a -> IterateeGM elfrom m a -- | A TIFF directory is a finite map associating a TIFF tag with a record -- TIFFDE type TIFFDict = IntMap TIFFDE data TIFFDE TIFFDE :: Int -> TIFFDE_ENUM -> TIFFDE tiffde_count :: TIFFDE -> Int tiffde_enum :: TIFFDE -> TIFFDE_ENUM data TIFFDE_ENUM TEN_CHAR :: (forall a. EnumeratorGMM Word8 Char RBIO a) -> TIFFDE_ENUM TEN_BYTE :: (forall a. EnumeratorGMM Word8 Word8 RBIO a) -> TIFFDE_ENUM TEN_INT :: (forall a. EnumeratorGMM Word8 Integer RBIO a) -> TIFFDE_ENUM TEN_RAT :: (forall a. EnumeratorGMM Word8 Rational RBIO a) -> TIFFDE_ENUM -- | Standard TIFF data types data TIFF_TYPE TT_NONE :: TIFF_TYPE TT_byte :: TIFF_TYPE TT_ascii :: TIFF_TYPE TT_short :: TIFF_TYPE TT_long :: TIFF_TYPE TT_rational :: TIFF_TYPE TT_sbyte :: TIFF_TYPE TT_undefined :: TIFF_TYPE TT_sshort :: TIFF_TYPE TT_slong :: TIFF_TYPE TT_srational :: TIFF_TYPE TT_float :: TIFF_TYPE TT_double :: TIFF_TYPE -- | Standard TIFF tags data TIFF_TAG TG_other :: Int -> TIFF_TAG TG_SUBFILETYPE :: TIFF_TAG TG_OSUBFILETYPE :: TIFF_TAG TG_IMAGEWIDTH :: TIFF_TAG TG_IMAGELENGTH :: TIFF_TAG TG_BITSPERSAMPLE :: TIFF_TAG TG_COMPRESSION :: TIFF_TAG TG_PHOTOMETRIC :: TIFF_TAG TG_THRESHOLDING :: TIFF_TAG TG_CELLWIDTH :: TIFF_TAG TG_CELLLENGTH :: TIFF_TAG TG_FILLORDER :: TIFF_TAG TG_DOCUMENTNAME :: TIFF_TAG TG_IMAGEDESCRIPTION :: TIFF_TAG TG_MAKE :: TIFF_TAG TG_MODEL :: TIFF_TAG TG_STRIPOFFSETS :: TIFF_TAG TG_ORIENTATION :: TIFF_TAG TG_SAMPLESPERPIXEL :: TIFF_TAG TG_ROWSPERSTRIP :: TIFF_TAG TG_STRIPBYTECOUNTS :: TIFF_TAG TG_MINSAMPLEVALUE :: TIFF_TAG TG_MAXSAMPLEVALUE :: TIFF_TAG TG_XRESOLUTION :: TIFF_TAG TG_YRESOLUTION :: TIFF_TAG TG_PLANARCONFIG :: TIFF_TAG TG_PAGENAME :: TIFF_TAG TG_XPOSITION :: TIFF_TAG TG_YPOSITION :: TIFF_TAG TG_FREEOFFSETS :: TIFF_TAG TG_FREEBYTECOUNTS :: TIFF_TAG TG_GRAYRESPONSEUNIT :: TIFF_TAG TG_GRAYRESPONSECURVE :: TIFF_TAG TG_GROUP3OPTIONS :: TIFF_TAG TG_GROUP4OPTIONS :: TIFF_TAG TG_RESOLUTIONUNIT :: TIFF_TAG TG_PAGENUMBER :: TIFF_TAG TG_COLORRESPONSEUNIT :: TIFF_TAG TG_COLORRESPONSECURVE :: TIFF_TAG TG_SOFTWARE :: TIFF_TAG TG_DATETIME :: TIFF_TAG TG_ARTIST :: TIFF_TAG TG_HOSTCOMPUTER :: TIFF_TAG TG_PREDICTOR :: TIFF_TAG TG_WHITEPOINT :: TIFF_TAG TG_PRIMARYCHROMATICITIES :: TIFF_TAG TG_COLORMAP :: TIFF_TAG TG_BADFAXLINES :: TIFF_TAG TG_CLEANFAXDATA :: TIFF_TAG TG_CONSECUTIVEBADFAXLINES :: TIFF_TAG TG_MATTEING :: TIFF_TAG tag_to_int :: TIFF_TAG -> Int int_to_tag :: Int -> TIFF_TAG -- | The library function to read the TIFF dictionary tiff_reader :: IterateeGM Word8 RBIO (Maybe TIFFDict) -- | A few conversion procedures u32_to_float :: Word32 -> Double u32_to_s32 :: Word32 -> Int32 u16_to_s16 :: Word16 -> Int16 u8_to_s8 :: Word8 -> Int8 note :: [String] -> IterateeGM el RBIO () -- | An internal function to load the dictionary. It assumes that the -- stream is positioned to read the dictionary load_dict :: IterateeGM Word8 RBIO (Maybe TIFFDict) -- | Reading the pixel matrix For simplicity, we assume no compression and -- 8-bit pixels pixel_matrix_enum :: TIFFDict -> EnumeratorN Word8 Word8 RBIO a -- | A few helpers for getting data from TIFF dictionary dict_read_int :: TIFF_TAG -> TIFFDict -> IterateeGM Word8 RBIO (Maybe Integer) dict_read_ints :: TIFF_TAG -> TIFFDict -> IterateeGM Word8 RBIO (Maybe [Integer]) dict_read_rat :: TIFF_TAG -> TIFFDict -> IterateeGM Word8 RBIO (Maybe Rational) dict_read_string :: TIFF_TAG -> TIFFDict -> IterateeGM Word8 RBIO (Maybe String) instance Eq TIFF_TAG instance Show TIFF_TAG instance Eq TIFF_TYPE instance Enum TIFF_TYPE instance Ord TIFF_TYPE instance Bounded TIFF_TYPE instance Show TIFF_TYPE -- | http://okmij.org/ftp/Haskell/misc.html#catch-MonadIO -- -- The ability to use functions catch, bracket, -- catchDyn, etc. in MonadIO other than IO itself has been a -- fairly frequently requested feature: -- -- -- http://www.haskell.org/pipermail/glasgow-haskell-users/2003-September/005660.html -- -- -- http://haskell.org/pipermail/libraries/2003-February/000774.html -- -- The reason it is not implemented is because these functions cannot be -- defined for a general MonadIO. However, these functions can be easily -- defined for a large and interesting subset of MonadIO. The following -- code demonstrates that. It uses no extensions (other than those needed -- for the Monad Transformer Library itself), patches no compilers, and -- proposes no extensions. The generic catch has been useful in a -- database library (Takusen), where many operations work in a monad -- (ReaderT Session IO): IO with the environment containing the database -- session data. Many other foreign libraries have a pattern of passing -- around various handles, which are better hidden in a monad. Still, we -- should be able to handle IO errors and user exceptions that arise in -- these computations. module Control.CaughtMonadIO data MyException MyException :: String -> MyException -- | The implementation is quite trivial. class (MonadIO m) => CaughtMonadIO m gcatch :: (CaughtMonadIO m) => m a -> (Exception -> m a) -> m a catchDyn :: (Typeable e, CaughtMonadIO m) => m a -> (e -> m a) -> m a instance Typeable MyException instance Show MyException instance (Monoid w, CaughtMonadIO m) => CaughtMonadIO (RWST r w s m) instance (CaughtMonadIO m) => CaughtMonadIO (StateT s m) instance (Monoid w, CaughtMonadIO m) => CaughtMonadIO (WriterT w m) instance (CaughtMonadIO m) => CaughtMonadIO (ReaderT r m) instance (CaughtMonadIO m, Error e) => CaughtMonadIO (ErrorT e m) instance CaughtMonadIO IO -- | Haskell98 -- -- http://okmij.org/ftp/Algorithms.html#pure-cyclic-list -- -- Pure functional, mutation-free, constant-time-access double-linked -- lists -- -- Note that insertions, deletions, lookups have a worst-case complexity -- of O(min(n,W)), where W is either 32 or 64 (depending on the -- paltform). That means the access time is bounded by a small constant -- (32 or 64). -- -- Pure functional, mutation-free, efficient double-linked lists -- -- It is always an interesting challenge to write a pure functional and -- efficient implementation of an imperative algorithm destructively -- operating a data structure. The functional implementation has a -- significant benefit of equational reasoning and modularity. We can -- comprehend the algorithm without keeping the implicit global state in -- mind. The mutation-free, functional realization has practical -- benefits: the ease of adding checkpointing, undo and redo. The absence -- of mutations makes the code multi-threading-safe and helps in porting -- to distributed or non-shared-memory parallel architectures. On the -- other hand, an imperative implementation has the advantage of -- optimality: mutating a component in a complex data structure is a -- constant-time operation, at least on conventional architectures. -- Imperative code makes sharing explicit, and so permits efficient -- implementation of cyclic data structures. -- -- We show a simple example of achieving all the benefits of an -- imperative data structure -- including sharing and the efficiency of -- updates -- in a pure functional program. Our data structure is a -- doubly-linked, possibly cyclic list, with the standard operations of -- adding, deleting and updating elements; traversing the list in both -- directions; iterating over the list, with cycle detection. The code: -- -- uniformly handles both cyclic and terminated lists; does not -- rebuild the whole list on updates; updates the value in the current -- node in time bound by a small constant; does not use or mention any -- monads; does not use any IORef, STRef, TVars, or any other -- destructive updates; permits the logging, undoing and redoing of -- updates, checkpointing; easily generalizes to two-dimensional -- meshes. -- -- The algorithm is essentially imperative, thus permitting identity -- checking and in-place updates, but implemented purely -- functionally. Although the code uses many local, type safe -- heaps, there is emphatically no global heap and no global -- state. -- -- Version: The current version is 1.2, Jan 7, 2009. -- -- References -- -- Haskell-Cafe discussion ``Updating doubly linked lists''. January 2009 module Data.FDList -- | Representation of the double-linked list type Ref = Int data Node a Node :: a -> Ref -> Ref -> Node a node_val :: Node a -> a node_left :: Node a -> Ref node_right :: Node a -> Ref -- | Because DList contains the pointer to the current element, -- DList is also a Zipper data DList a DList :: Ref -> Ref -> IntMap (Node a) -> DList a dl_counter :: DList a -> Ref dl_current :: DList a -> Ref dl_mem :: DList a -> IntMap (Node a) -- | Operations on the DList a empty :: DList a -- | In a well-formed list, dl_current must point to a valid node All -- operations below preserve well-formedness well_formed :: DList a -> Bool is_empty :: DList a -> Bool -- | auxiliary function get_curr_node :: DList a -> Node a -- | The insert operation below makes a cyclic list The other operations -- don't care Insert to the right of the current element, if any Return -- the DL where the inserted node is the current one insert_right :: a -> DList a -> DList a -- | Delete the current element from a non-empty list We can handle both -- cyclic and terminated lists The right node becomes the current node. -- If the right node does not exists, the left node becomes current delete :: DList a -> DList a get_curr :: DList a -> a move_right :: DList a -> Maybe (DList a) -- | If no right, just stay inplace move_right' :: DList a -> DList a move_left :: DList a -> Maybe (DList a) -- | If no left, just stay inplace move_left' :: DList a -> DList a fromList :: [a] -> DList a -- | The following does not anticipate cycles (deliberately) takeDL :: Int -> DList a -> [a] -- | Reverse taking: we move left takeDLrev :: Int -> DList a -> [a] -- | Update the current node inplace update :: a -> DList a -> DList a -- | This one watches for a cycle and terminates when it detects one toList :: DList a -> [a]