<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5981432593633653194</id><updated>2011-10-24T23:53:47.122+01:00</updated><category term='Random'/><category term='csound'/><category term='gtk2hs'/><category term='Iteratee'/><category term='x-dsp'/><category term='Type Theory'/><category term='IO'/><category term='haskell'/><category term='OpenGL'/><category term='Music'/><category term='FRP'/><title type='text'>Sound and Software</title><subtitle type='html'>Explorations of audio pragramming with Haskell</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>18</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-2452312003825951331</id><published>2011-10-11T13:34:00.000+01:00</published><updated>2011-10-11T13:34:23.102+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Type Theory'/><category scheme='http://www.blogger.com/atom/ns#' term='Random'/><title type='text'>J. Roger Hindley is coming to Maynooth</title><content type='html'>I have recently learned that &lt;a href="http://www-maths.swan.ac.uk/staff/jrh/"&gt;J. Roger Hindley&lt;/a&gt; (of &lt;a href="http://en.wikipedia.org/wiki/Hindley%E2%80%93Milner"&gt;Hindley-Milner&lt;/a&gt;) will be &lt;a href="http://www.maths.nuim.ie/colloquia"&gt;giving a talk&lt;/a&gt; next month on the history of Curry-Howard in the Mathematics Dept. at NUI Maynooth.&amp;nbsp; I will definitely be there!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-2452312003825951331?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/2452312003825951331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2011/10/j-roger-hindley-is-coming-to-maynooth.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/2452312003825951331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/2452312003825951331'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2011/10/j-roger-hindley-is-coming-to-maynooth.html' title='J. Roger Hindley is coming to Maynooth'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-1028731422863109391</id><published>2011-09-16T23:39:00.000+01:00</published><updated>2011-09-16T23:39:23.880+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><title type='text'>splaytree package released</title><content type='html'>I've just uploaded a new package to Hackage, &lt;a href="http://hackage.haskell.org/package/splaytree"&gt;splaytree-0.1&lt;/a&gt;.&amp;nbsp; Splaytree provides pure functional splay trees, implemented using monoidal annotations.&amp;nbsp; A splay tree is a self-balancing tree with the property that recently-accessed nodes remain near the root, and thus subsequent accesses can be quite cheap.&amp;nbsp; As for the monoid annotations, it's the same technique as used in finger trees (see &lt;a href="http://www.soi.city.ac.uk/%7Eross/papers/FingerTree.html"&gt;here&lt;/a&gt;, &lt;a href="http://apfelmus.nfshost.com/articles/monoid-fingertree.html"&gt;here&lt;/a&gt;, and &lt;a href="http://scienceblogs.com/goodmath/2010/04/finger_trees_done_right_i_hope.php"&gt;here&lt;/a&gt;).&amp;nbsp; The idea is that each node of a tree holds an annotation in addition to data.&amp;nbsp; If the type of the annotation is chosen properly, it can be used to prune branches when searching.&amp;nbsp; One clever aspect of this technique is that different data structures can be implemented simply by choosing different annotations.&lt;br /&gt;&lt;br /&gt;Currently three different data structures are available, Data.SplayTree.Set, Data.SplayTree.Seq, and Data.SplayTree.RangeSet.&amp;nbsp; 'Set' provides a set with standard operations.&amp;nbsp; Not much time has been spent optimizing this library, but performance is competitive with unordered-containers for many operations (inserts can be quite slow though).&amp;nbsp; Seq is a sequence type, similar to Data.Seq.&lt;br /&gt;&lt;br /&gt;RangeSet is perhaps the most interesting of the available containers.&amp;nbsp; Similar to an IntervalSet, a RangeSet is used for storing a list of Ranges.&amp;nbsp; A RangeSet, however, guarantees that all ranges are non-overlapping.&amp;nbsp; When a new range is inserted into the RangeSet, it is combined with any ranges currently in the set with which it overlaps.&lt;br /&gt;&lt;br /&gt;Example (Note that a Range is a starting point and duration):&lt;br /&gt;&lt;pre class="source-code"&gt;&lt;code&gt;&lt;br /&gt;Prelude Data.SplayTree.RangeSet Data.Foldable&amp;gt; let rs = singleton (range 0 4)&lt;br /&gt;Prelude Data.SplayTree.RangeSet Data.Foldable&amp;gt; toList rs&lt;br /&gt;[Range {rMin = 0, rang = 4}]&lt;br /&gt;Prelude Data.SplayTree.RangeSet Data.Foldable&amp;gt; toList $ insert rs (range 3 2)&lt;br /&gt;[Range {rMin = 0, rang = 5}]&lt;br /&gt;Prelude Data.SplayTree.RangeSet Data.Foldable&amp;gt; &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Comments and patches welcome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-1028731422863109391?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/1028731422863109391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2011/09/splaytree-package-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/1028731422863109391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/1028731422863109391'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2011/09/splaytree-package-released.html' title='splaytree package released'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-5088313930430767984</id><published>2011-09-02T13:00:00.000+01:00</published><updated>2011-09-02T13:00:28.722+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><title type='text'>My mind is blown</title><content type='html'>&lt;br /&gt;Recently I've read two blog posts which have really started to change how I think about programming. &amp;nbsp;A while ago I found a &lt;a href="http://conway.rutgers.edu/~ccshan/wiki/blog/posts/WordNumbers1/"&gt;solution&lt;/a&gt; to the word numbers problem which completely stunned me with its simplicity and generality. Today I stumbled upon Apfelmus's "monoids and finger trees" &lt;a href="http://apfelmus.nfshost.com/articles/monoid-fingertree.html"&gt;blog post&lt;/a&gt;, which is very timely since I can use the same approach to both simplify and generalize a data structure I've been working on recently.&lt;br /&gt;&lt;br /&gt;With this in mind, I would like to move from my current awareness (being able to follow along with these discussions) to being able to recognize and apply very general structures. &amp;nbsp;Somewhat tongue-in-cheek, I'd like to know how to make good use of &lt;a href="http://comonad.com/reader/"&gt;Edward Kmett&lt;/a&gt;'s packages. &amp;nbsp;Advice would be appreciated.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-5088313930430767984?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/5088313930430767984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2011/09/my-mind-is-blown.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/5088313930430767984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/5088313930430767984'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2011/09/my-mind-is-blown.html' title='My mind is blown'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-5821495273367968802</id><published>2011-07-06T21:09:00.002+01:00</published><updated>2011-07-07T01:22:39.523+01:00</updated><title type='text'>Circular Buffers</title><content type='html'>An extremely useful tool in DSP programming is the &lt;a href="http://en.wikipedia.org/wiki/Circular_buffer"&gt;&lt;i&gt;circular buffer&lt;/i&gt;&lt;/a&gt;.&amp;nbsp; Basically, a circular buffer is useful for keeping an array of &lt;i&gt;n&lt;/i&gt; elements of a data stream.&amp;nbsp; Implementing this in Haskell is not straightforward, because the usual implementation relies upon mutating arrays.&amp;nbsp; As Haskell is a pure language, mutable data can only be used in restricted contexts, which can make a mutable circular buffer somewhat awkward.&amp;nbsp; It offends my sensibilities if nothing else.&lt;br /&gt;&lt;br /&gt;(Code presented in this article is basically the first thing I thought of.&amp;nbsp; Improvements are welcome, provided they don't detract too much from readability.&amp;nbsp; All code is placed in the public domain.) &lt;br /&gt;&lt;br /&gt;The ideal Haskell implementation would be pure, and for certain cases a high-performance solution is available.&amp;nbsp; If you only need access to the end of the buffer (i.e. a FIFO queue),&amp;nbsp; real-time pure functional implementations are well-known (&lt;a href="http://www.amazon.com/Purely-Functional-Structures-Chris-Okasaki/dp/0521663504"&gt;Okasaki&lt;/a&gt;).&amp;nbsp; But what if you want to read from the middle of the buffer?&amp;nbsp; In an imperative implementation, this is usually handled by either keeping multiple read pointers, or by using an absolute offset to the start of the buffer.&amp;nbsp; It's not clear how to apply either of these techniques to Okasaki's queue implementation though, and the obvious approach of dequeueing as many times as required has O(n) complexity.&lt;br /&gt;&lt;br /&gt;An obvious approach is to write an implementation using mutable data.&amp;nbsp; The implementation would no longer be pure unfortunately.&amp;nbsp; Here's a simple implementation which runs in IO, based upon &lt;a href="http://hackage.haskell.org/packages/archive/vector/0.7.1/doc/html/Data-Vector-Unboxed-Mutable.html"&gt;mutable vectors&lt;/a&gt;:&lt;br /&gt;&lt;pre class="source-code"&gt;&lt;code&gt;&lt;br /&gt;module Data.RingBuffer.Mutable (&lt;br /&gt;  RingBuffer&lt;br /&gt; ,newInit&lt;br /&gt; ,push&lt;br /&gt; ,length&lt;br /&gt; ,(!)&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;where&lt;br /&gt;&lt;br /&gt;import qualified Data.Vector.Unboxed.Mutable as V&lt;br /&gt;&lt;br /&gt;import Foreign.ForeignPtr&lt;br /&gt;import Foreign.Storable&lt;br /&gt;import Control.Applicative&lt;br /&gt;import GHC.Prim&lt;br /&gt;&lt;br /&gt;data RingBuffer a = RB (V.MVector RealWorld a) (ForeignPtr Int)&lt;br /&gt;&lt;br /&gt;newFp :: Storable a =&amp;gt; a -&amp;gt; IO (ForeignPtr a)&lt;br /&gt;newFp a = do&lt;br /&gt;  fp &amp;lt;- mallocForeignPtr&lt;br /&gt;  withForeignPtr fp $ \p -&amp;gt; poke p a&lt;br /&gt;  return fp&lt;br /&gt;&lt;br /&gt;readFp :: Storable a =&amp;gt; ForeignPtr a -&amp;gt; IO a&lt;br /&gt;readFp fp = withForeignPtr fp $ \p -&amp;gt; peek p&lt;br /&gt;&lt;br /&gt;writeFp fp a = withForeignPtr fp $ \p -&amp;gt; poke p a&lt;br /&gt;&lt;br /&gt;newInit :: (V.Unbox a, Num a) =&amp;gt; Int -&amp;gt; IO (RingBuffer a)&lt;br /&gt;newInit sz = RB &amp;lt;$&amp;gt; (V.replicate sz 0) &amp;lt;*&amp;gt; newFp 0&lt;br /&gt;{-# INLINE newInit #-}&lt;br /&gt;&lt;br /&gt;(!) :: (V.Unbox a) =&amp;gt; RingBuffer a -&amp;gt; Int -&amp;gt; IO a&lt;br /&gt;(!) (RB vec ref) ix = do&lt;br /&gt;  off &amp;lt;- readFp ref&lt;br /&gt;  let len = V.length vec&lt;br /&gt;  V.unsafeRead vec ((ix+off) `rem` len)&lt;br /&gt;{-# INLINE (!) #-}&lt;br /&gt;&lt;br /&gt;push :: V.Unbox a =&amp;gt; RingBuffer a -&amp;gt; a -&amp;gt; IO ()&lt;br /&gt;push (RB vec ref) el = do&lt;br /&gt;  off &amp;lt;- readFp ref&lt;br /&gt;  let len = V.length vec&lt;br /&gt;  let off' = (off+1) `rem` len&lt;br /&gt;  writeFp ref off'&lt;br /&gt;  V.unsafeWrite vec off el&lt;br /&gt;{-# INLINE push #-}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;So what would an efficient pure implementation look like?&lt;br /&gt;&lt;br /&gt;Probably the fanciest pure data structure is the &lt;a href="http://en.wikipedia.org/wiki/Finger_tree"&gt;finger tree&lt;/a&gt;, most commonly known by the implementation in &lt;b&gt;Data.Sequence.Seq&lt;/b&gt;.&amp;nbsp; Finger trees have many interesting attributes, including O(1) access to both ends.&amp;nbsp; They do not have O(1) access to interior elements, but better than O(n).&amp;nbsp; It's pretty simple to implement a similar interface with a Seq:&lt;br /&gt;&lt;pre class="source-code"&gt;&lt;code&gt;&lt;br /&gt;module Data.RingBuffer (&lt;br /&gt;  RingBuffer&lt;br /&gt; ,new&lt;br /&gt; ,newInit&lt;br /&gt; ,push&lt;br /&gt; ,length&lt;br /&gt; ,(!)&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;where&lt;br /&gt;&lt;br /&gt;import           Prelude hiding (length)&lt;br /&gt;&lt;br /&gt;import qualified Data.Sequence as S&lt;br /&gt;&lt;br /&gt;newtype RingBuffer a = RB (S.Seq a) deriving (Eq, Ord, Show)&lt;br /&gt;&lt;br /&gt;-- | Create a new RingBuffer, initialized to all 0's, of the given size&lt;br /&gt;new :: (Num a) =&amp;gt; Int -&amp;gt; RingBuffer a&lt;br /&gt;new = newInit 0&lt;br /&gt;{-# INLINE new #-}&lt;br /&gt;&lt;br /&gt;-- | Create a new RingBuffer from a given initial value&lt;br /&gt;newInit :: a -&amp;gt; Int -&amp;gt; RingBuffer a&lt;br /&gt;newInit i sz | sz &amp;lt;= 0 = error "can't make empty ringbuffer"&lt;br /&gt;newInit i sz           = RB (S.replicate sz i)&lt;br /&gt;{-# INLINE newInit #-}&lt;br /&gt;&lt;br /&gt;-- | Get the total size of a RingBuffer.&lt;br /&gt;length :: RingBuffer a -&amp;gt; Int&lt;br /&gt;length (RB vec) = S.length vec&lt;br /&gt;{-# INLINE length #-}&lt;br /&gt;&lt;br /&gt;-- | Look up a value in a RingBuffer.&lt;br /&gt;(!) :: RingBuffer a -&amp;gt; Int -&amp;gt; a&lt;br /&gt;(!) (RB vec) = S.index vec&lt;br /&gt;{-# INLINE (!) #-}&lt;br /&gt;&lt;br /&gt;-- | Push a new value into a RingBuffer.  The following will hold:&lt;br /&gt;--     NewRingBuffer ! 0 === added element&lt;br /&gt;--     NewRingBuffer ! 1 === OldRingBuffer ! 0&lt;br /&gt;push :: RingBuffer a -&amp;gt; a -&amp;gt; RingBuffer a&lt;br /&gt;push (RB vec) el   = case S.viewr vec of&lt;br /&gt;  v' S.:&amp;gt; _ -&amp;gt; RB $ el S.&amp;lt;| v'&lt;br /&gt;  _         -&amp;gt; error "internal error"&lt;br /&gt;{-# INLINE push #-}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Ok, so it's not quite the same, but close enough.&lt;br /&gt;&lt;br /&gt;So how does this compare, performance-wise?&amp;nbsp; I whipped up some criterion code to check it out.&lt;br /&gt;&lt;br /&gt;&lt;pre class="source-code"&gt;&lt;code&gt;&lt;br /&gt;module Main where&lt;br /&gt;&lt;br /&gt;import Criterion.Main&lt;br /&gt;import System.Random&lt;br /&gt;import Control.Monad&lt;br /&gt;&lt;br /&gt;import qualified Data.RingBuffer.Mutable as RM&lt;br /&gt;import qualified Data.RingBuffer as R&lt;br /&gt;import Data.Vector.Unboxed (Unbox)&lt;br /&gt;import System.IO.Unsafe&lt;br /&gt;&lt;br /&gt;rndSeed = 49872&lt;br /&gt;&lt;br /&gt;-- test performance as a queue&lt;br /&gt;queueMut :: (Unbox a, Num a) =&amp;gt; Int -&amp;gt; Int -&amp;gt; a -&amp;gt; IO ()&lt;br /&gt;queueMut reps sz a = do&lt;br /&gt;  buf &amp;lt;- RM.newInit sz&lt;br /&gt;  replicateM_ reps (buf RM.! (sz-1) &amp;gt;&amp;gt; RM.push buf a)&lt;br /&gt;&lt;br /&gt;-- | test performance of multiple reads&lt;br /&gt;multireadMut :: (Unbox a, Num a) =&amp;gt; Int -&amp;gt; Int -&amp;gt; a -&amp;gt; IO ()&lt;br /&gt;multireadMut nReads sz a = do&lt;br /&gt;  let rs = take nReads $ randomRs (0,sz-1) $ mkStdGen rndSeed&lt;br /&gt;  buf &amp;lt;- RM.newInit sz&lt;br /&gt;  replicateM_ iters $ do&lt;br /&gt;    mapM_ (\ix -&amp;gt; buf RM.! ix) rs&lt;br /&gt;    RM.push buf a&lt;br /&gt;&lt;br /&gt;queueP :: Int -&amp;gt; Int -&amp;gt; a -&amp;gt; a&lt;br /&gt;queueP reps sz a = fst $ (iterate go (a, R.newInit a sz)) !! reps&lt;br /&gt; where&lt;br /&gt;  go (a', buf) = a' `seq` (buf R.! (sz-1), R.push buf a)&lt;br /&gt;&lt;br /&gt;multireadP :: Int -&amp;gt; Int -&amp;gt; a -&amp;gt; [a]&lt;br /&gt;multireadP nReads sz a = fst $ (iterate go (replicate nReads a, R.newInit a sz)) !! iters&lt;br /&gt; where&lt;br /&gt;  go (last, buf) = length last `seq` (map (buf R.!) rs, R.push buf a)&lt;br /&gt;  rs = take nReads $ randomRs (0,sz-1) $ mkStdGen rndSeed&lt;br /&gt;  &lt;br /&gt;&lt;br /&gt;numReps = 100&lt;br /&gt;numReads = 50&lt;br /&gt;&lt;br /&gt;iters = 10000&lt;br /&gt;&lt;br /&gt;main = defaultMain&lt;br /&gt;  [&lt;br /&gt;   bgroup "Mutable/Int" [&lt;br /&gt;     bgroup "Queue" [&lt;br /&gt;       bench "8"      $ queueMut numReps 8     (10::Int)&lt;br /&gt;      ,bench "128"    $ queueMut numReps 128    (10::Int)&lt;br /&gt;      ,bench "1024"   $ queueMut numReps 1024   (10::Int)&lt;br /&gt;      ,bench "10240"  $ queueMut numReps 10240  (10::Int)&lt;br /&gt;      ,bench "102400" $ queueMut numReps 102400 (10::Int)&lt;br /&gt;     ]&lt;br /&gt;     ,bgroup "Multiread" [&lt;br /&gt;       bench "8"     $ multireadMut numReads 8     (10::Int)&lt;br /&gt;      ,bench "128"    $ multireadMut numReads 128    (10::Int)&lt;br /&gt;      ,bench "1024"   $ multireadMut numReads 1024   (10::Int)&lt;br /&gt;      ,bench "10240"  $ multireadMut numReads 10240  (10::Int)&lt;br /&gt;      ,bench "102400" $ multireadMut numReads 102400 (10::Int)&lt;br /&gt;      ]&lt;br /&gt;   ]&lt;br /&gt;  ,bgroup "Mutable/Double" [&lt;br /&gt;     bgroup "Queue" [&lt;br /&gt;       bench "8"     $ queueMut numReps 8     (10::Double)&lt;br /&gt;      ,bench "128"    $ queueMut numReps 128    (10::Double)&lt;br /&gt;      ,bench "1024"   $ queueMut numReps 1024   (10::Double)&lt;br /&gt;      ,bench "10240"  $ queueMut numReps 10240  (10::Double)&lt;br /&gt;      ,bench "102400" $ queueMut numReps 102400 (10::Double)&lt;br /&gt;     ]&lt;br /&gt;     ,bgroup "Multiread" [&lt;br /&gt;       bench "8"     $ multireadMut numReads 8     (10::Double)&lt;br /&gt;      ,bench "128"    $ multireadMut numReads 128    (10::Double)&lt;br /&gt;      ,bench "1024"   $ multireadMut numReads 1024   (10::Double)&lt;br /&gt;      ,bench "10240"  $ multireadMut numReads 10240  (10::Double)&lt;br /&gt;      ,bench "102400" $ multireadMut numReads 102400 (10::Double)&lt;br /&gt;     ]&lt;br /&gt;   ]&lt;br /&gt;  ,bgroup "Pure/Int" [&lt;br /&gt;     bgroup "Queue" [&lt;br /&gt;       bench "8"     $ whnf (queueP numReps 8)     (10::Int)&lt;br /&gt;      ,bench "128"    $ whnf (queueP numReps 128)    (10::Int)&lt;br /&gt;      ,bench "1024"   $ whnf (queueP numReps 1024)   (10::Int)&lt;br /&gt;      ,bench "10240"  $ whnf (queueP numReps 10240)  (10::Int)&lt;br /&gt;      ,bench "102400" $ whnf (queueP numReps 102400) (10::Int)&lt;br /&gt;     ]&lt;br /&gt;     ,bgroup "Multiread" [&lt;br /&gt;       bench "8"     $ nf (multireadP numReads 8)     (10::Int)&lt;br /&gt;      ,bench "128"    $ nf (multireadP numReads 128)    (10::Int)&lt;br /&gt;      ,bench "1024"   $ nf (multireadP numReads 1024)   (10::Int)&lt;br /&gt;      ,bench "10240"  $ nf (multireadP numReads 10240)  (10::Int)&lt;br /&gt;      ,bench "102400" $ nf (multireadP numReads 102400) (10::Int)&lt;br /&gt;     ]&lt;br /&gt;   ]&lt;br /&gt;  ,bgroup "Pure/Double" [&lt;br /&gt;     bgroup "Queue" [&lt;br /&gt;       bench "8"     $ whnf (queueP numReps 8)     (10::Double)&lt;br /&gt;      ,bench "128"    $ whnf (queueP numReps 128)    (10::Double)&lt;br /&gt;      ,bench "1024"   $ whnf (queueP numReps 1024)   (10::Double)&lt;br /&gt;      ,bench "10240"  $ whnf (queueP numReps 10240)  (10::Double)&lt;br /&gt;      ,bench "102400" $ whnf (queueP numReps 102400) (10::Double)&lt;br /&gt;     ]&lt;br /&gt;     ,bgroup "Multiread" [&lt;br /&gt;       bench "8"     $ nf (multireadP numReads 8)     (10::Double)&lt;br /&gt;      ,bench "128"    $ nf (multireadP numReads 128)    (10::Double)&lt;br /&gt;      ,bench "1024"   $ nf (multireadP numReads 1024)   (10::Double)&lt;br /&gt;      ,bench "10240"  $ nf (multireadP numReads 10240)  (10::Double)&lt;br /&gt;      ,bench "102400" $ nf (multireadP numReads 102400) (10::Double)&lt;br /&gt;     ]&lt;br /&gt;   ]&lt;br /&gt;  ]&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;A few points about the benchmarks.&amp;nbsp; First, both queue and multiread perform multiple cycles in each benchmark.&amp;nbsp; Secondly, the pure benchmarks both &lt;i&gt;seq&lt;/i&gt; outputs at each cycle to ensure that work is actually performed.&amp;nbsp; So how do these implementations compare?&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-wF_8jGgooXg/ThS_QB_SAfI/AAAAAAAAAAU/kF1A37wAR88/s1600/image002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-wF_8jGgooXg/ThS_QB_SAfI/AAAAAAAAAAU/kF1A37wAR88/s1600/image002.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-wt35sAaSZBo/ThS_TkIT4CI/AAAAAAAAAAY/7jRIwGuLLOY/s1600/image001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-wt35sAaSZBo/ThS_TkIT4CI/AAAAAAAAAAY/7jRIwGuLLOY/s1600/image001.png" /&gt;&lt;/a&gt;&lt;/div&gt;It looks like not only is the Seq-based implementation pure, it outperforms my mutable-vector implementation and scales better too.&amp;nbsp; Completely unexpected.&lt;br /&gt;&lt;br /&gt;Unless somebody points out a major flaw in my testing methodology or implementations, I'll be using Data.Sequence.Seq for my circular buffer needs from now on.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://spreadsheets.google.com/spreadsheet/ccc?key=0ArqRcG_mlfL4dE0xOUNvZzdfMHdQN280SG1FTjRHRVE&amp;amp;hl=en_US"&gt;Benchmark data&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Edit: thanks to Henning Thielemann for suggested improvements.&amp;nbsp; I've updated the benchmark code with his suggestions regarding mkStdGen.&amp;nbsp; I also have updated the mutable buffer to use a ForeignPtr to hold the offset rather than an IORef.&amp;nbsp; The performance is better, but not drastically so.  I haven't put up new images yet, although I have uploaded the new benchmark data.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-5821495273367968802?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/5821495273367968802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2011/07/circular-buffers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/5821495273367968802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/5821495273367968802'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2011/07/circular-buffers.html' title='Circular Buffers'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-wF_8jGgooXg/ThS_QB_SAfI/AAAAAAAAAAU/kF1A37wAR88/s72-c/image002.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-9108118612572041512</id><published>2011-05-06T14:14:00.003+01:00</published><updated>2011-05-06T14:15:01.745+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Music'/><category scheme='http://www.blogger.com/atom/ns#' term='Random'/><title type='text'>Linux Audio Conference 2011</title><content type='html'>Streams are live now. &amp;nbsp;Check it out if you couldn't make it in person.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://streamer.stackingdwarves.net/"&gt;http://streamer.stackingdwarves.net/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-9108118612572041512?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/9108118612572041512/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2011/05/linux-audio-conference-2011.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/9108118612572041512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/9108118612572041512'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2011/05/linux-audio-conference-2011.html' title='Linux Audio Conference 2011'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-4247882869607946691</id><published>2011-05-05T01:20:00.000+01:00</published><updated>2011-05-05T01:20:33.260+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gtk2hs'/><title type='text'>GTK on OSX</title><content type='html'>I finally got tired of the default gtk theme on my macbook, so I decided to do something about it.&amp;nbsp; I've been using gtk2 no_x11 variant provided by macports, but unfortunately all the theme engines on macports require X11.&amp;nbsp; I tried the &lt;a href="http://sourceforge.net/apps/trac/gtk-osx/wiki/GtkQuartzEngine"&gt;GtkQuartzEngine&lt;/a&gt;, and it seems to work perfectly with a little tweaking.&lt;br /&gt;&lt;br /&gt;1.&amp;nbsp; Check out git://github.com/jralls/gtk-quartz-engine.git&lt;br /&gt;2.&amp;nbsp; Cd into the gtk-quartz-engine repo and run &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;aclocal &amp;amp;&amp;amp; &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;autoreconf -vi &amp;amp;&amp;amp; autoconf&lt;/span&gt; (I used the macports versions)&lt;br /&gt;3. &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;./configure --prefix=/opt/local &amp;amp;&amp;amp; make &amp;amp;&amp;amp; make install&lt;/span&gt;&lt;br /&gt;4.&amp;nbsp; Copy the sample gtkrc file to ~/.gtkrc-2.0.&lt;br /&gt;&lt;br /&gt;And it all just worked.&amp;nbsp; It looks decent, albeit slightly buggy.&amp;nbsp; It's necessary to set the prefix to /opt/local so the engine is installed into the same location as the macports gtk libs.&lt;br /&gt;&lt;br /&gt;This seems like a relatively simple candidate for adding to macports.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-4247882869607946691?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/4247882869607946691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2011/05/gtk-on-osx.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/4247882869607946691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/4247882869607946691'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2011/05/gtk-on-osx.html' title='GTK on OSX'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-7121294298304953735</id><published>2011-04-11T13:18:00.000+01:00</published><updated>2011-04-11T13:18:35.005+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='IO'/><category scheme='http://www.blogger.com/atom/ns#' term='FRP'/><title type='text'>Thoughts on FRP</title><content type='html'>(N.B. Code presented in this post is theoretical. &amp;nbsp;I haven't tried to compile it, although I do expect it would work.)&lt;br /&gt;&lt;br /&gt;Although various implementations have been under development for some time, &lt;a href="http://www.haskell.org/haskellwiki/Functional_Reactive_Programming"&gt;FRP&lt;/a&gt; has yet to reach a really satisfying breakthrough. &amp;nbsp;The best semantic model has been that of Conal Elliot's &lt;a href="http://haskell.org/haskellwiki/Reactive"&gt;Reactive&lt;/a&gt;, but unfortunately it lacks a Smart Enough Compiler and suffers from subtle space leaks. &amp;nbsp;The most performant is probably &lt;a href="http://www.haskell.org/haskellwiki/Yampa"&gt;Yampa&lt;/a&gt;, which is arrow-based. &amp;nbsp;Arrows turn some people off; I personally find them too verbose and don't like the syntax for recursion.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Apfelmus has recently released a new FRP library, &lt;a href="http://hackage.haskell.org/package/reactive-banana"&gt;reactive-banana&lt;/a&gt;, which shows some early promise towards being a best-of-all-possible-worlds. &amp;nbsp;In this post I'll examine some of the features of reactive-banana to see why I think this.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Introduction&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Reactive-banana is very similar to the semantic model provided by Reactive, although much more minimal. &amp;nbsp;One fundamental data type is the &lt;code&gt;Event&lt;/code&gt;, which can be thought of as a list of values with their time of occurrence, i.e. &lt;code&gt;Event a = [(Time, a)]&lt;/code&gt;. &amp;nbsp;The other fundamental type is the Behavior, which can be thought of as a value that changes with time, i.e. &lt;code&gt;Behavior a = \Time -&amp;gt; a&lt;/code&gt;. &amp;nbsp;This is according to the reactive-banana documentation, which also notes that in this implementation &lt;code&gt;Behavior&lt;/code&gt;s aren't continuous but are actually step functions.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So how does it look? &amp;nbsp;Consider a simple system with a button and a toggle: when the button is pressed it generates a 0 if the toggle is off, and 1 when the toggle is on. &amp;nbsp;We could model it like this:&lt;br /&gt;&lt;div&gt;&lt;pre class="source-code"&gt;&lt;code&gt;&lt;br /&gt;togEvent :: Event Bool&lt;br /&gt;buttonEvent :: Event ()&lt;br /&gt;&lt;br /&gt;-- Upon receiving an Event (), mixEvents generates an Event Int&lt;br /&gt;-- The value of the Int depends upon the most recent Event Bool status&lt;br /&gt;mixEvents :: Event Bool -&amp;gt; Event () -&amp;gt; Event Int&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;The &lt;code&gt;togEvent&lt;/code&gt; and &lt;code&gt;buttonEvent&lt;/code&gt; sources are created by connecting to the window framework's event system (see &lt;a href="http://hackage.haskell.org/package/reactive-banana-wx"&gt;reactive-banana-wx&lt;/a&gt; for an example), but &lt;code&gt;mixEvents&lt;/code&gt; is more complex. &amp;nbsp;We could model it with the pure function&lt;br /&gt;&lt;pre class="source-code"&gt;&lt;code&gt;&lt;br /&gt;mixEventsF :: Bool -&amp;gt; () -&amp;gt; Int&lt;br /&gt;mixEventsF True _ = 1&lt;br /&gt;mixEventsF _    _&amp;nbsp;= 0&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;so we just need to find a way to lift this into the FRP system. &amp;nbsp;Behaviors have an Applicative instance, so let's try it:&lt;br /&gt;&lt;pre class="source-code"&gt;&lt;code&gt;&lt;br /&gt;mixEvents = apply (mixEventsF &amp;lt;$&amp;gt; behavior False togEvent) buttonEvent&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;and we've got it. &amp;nbsp;If we prefer, we could define &lt;code&gt;mixEventsF :: Bool -&amp;gt; Int&lt;/code&gt;, but then we'd need to &lt;code&gt;fmap const&lt;/code&gt; onto the &lt;code&gt;Behavior&lt;/code&gt; to create a &lt;code&gt;Behavior (() -&amp;gt; Int)&lt;/code&gt; we could apply to an Event.&lt;br /&gt;&lt;br /&gt;So how does this work? &amp;nbsp;The &lt;code&gt;Behavior&lt;/code&gt; functions as an &lt;i&gt;Event Accumulator&lt;/i&gt;. &amp;nbsp;It stores every &lt;code&gt;Bool&lt;/code&gt; received from &lt;code&gt;togEvent&lt;/code&gt;, and when a button press event is received creates an event from applying the &lt;code&gt;Bool&lt;/code&gt; and &lt;code&gt;()&lt;/code&gt; to the &lt;code&gt;mixEventsF&lt;/code&gt; function. &amp;nbsp;One clue that this is the proper way to think of Behaviors is by the different ways they can be constructed:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;lifting pure values, and &lt;code&gt;always&lt;/code&gt; (these are semantically equivalent, &lt;code&gt;pure&lt;/code&gt; is defined as &lt;code&gt;always&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;the &lt;code&gt;behavior&lt;/code&gt; smart constructor (only hold the most recent &lt;code&gt;Event&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;various &lt;code&gt;accumulate&lt;/code&gt; functions on &lt;code&gt;Event&lt;/code&gt;s&lt;/li&gt;&lt;/ul&gt;So if a Behavior is an event accumulator, why do we need it at all? &amp;nbsp;Why not just use events directly, explicitly storing values when we need them? &amp;nbsp;In my view, the answer has to do with abstracting implementation from the user.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Push vs Pull&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;One tension in the FRP design space relates to a &lt;a href="http://conal.net/papers/push-pull-frp/"&gt;push-driven (data-driven) vs pull-driven (demand-driven)&lt;/a&gt; architecture. &amp;nbsp;Push-driven offer theoretically better performance, as data sources typically change relatively slowly (e.g. mouse clicks relative to screen refresh), so this minimizes recomputation. &amp;nbsp;However, pull-driven seems to fit the functional paradigm better. &amp;nbsp;Also, if a Behavior models a continuous function, pushing updates at the sample rate could be very wasteful.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;Behavior&lt;/code&gt; type (at least in reactive and reactive-banana) moderates between these two models. &amp;nbsp;We can see how this works by examining the semantics of our &lt;code&gt;mixEvents&lt;/code&gt; function.&lt;br /&gt;&lt;pre class="source-code"&gt;&lt;code&gt;&lt;br /&gt;-- Upon receiving an Event (), mixEvents generates an Event Int&lt;br /&gt;-- The value of the Int depends upon the most recent Event Bool status&lt;br /&gt;mixEvents :: Event Bool -&amp;gt; Event () -&amp;gt; Event Int&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Our implementation created a &lt;code&gt;Behavior (() -&amp;gt; Int)&lt;/code&gt; by combining a pure function with the &lt;code&gt;Event Bool&lt;/code&gt; stream. &amp;nbsp;This behavior is then sampled whenever an &lt;code&gt;Event ()&lt;/code&gt; is received. &amp;nbsp;By converting a discrete &lt;code&gt;Event&lt;/code&gt; into something which can be later sampled, the &lt;code&gt;Behavior&lt;/code&gt; localizes a change from push-driven to pull-driven. &amp;nbsp;Thus &lt;code&gt;Event Bool&lt;/code&gt;s are stored and pulled from as necessary, while &lt;code&gt;Event ()&lt;/code&gt;s are pushed through and become &lt;code&gt;Event Int&lt;/code&gt;s, which are then pushed until they're also changed into &lt;code&gt;Behaviors&lt;/code&gt; (or &lt;code&gt;reactimate&lt;/code&gt;'d).&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Continuous Time&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Another Behavior feature is that semantically (traditionally, but not in reactive-banana) they are continuous functions over time. &amp;nbsp;There are a &lt;a href="http://conal.net/blog/posts/why-program-with-continuous-time/"&gt;lot of reasons&lt;/a&gt; to like this feature, which is notably missing from the reactive-banana semantic model. &amp;nbsp;Originally I was somewhat skeptical, however I've come to the conclusion that the reactive-banana model of Behaviors as step functions is more correct. &amp;nbsp;Since a Behavior is an accumulator of Events, it makes sense that the Behavior would change only when new Events are received. &amp;nbsp;As an Event is a datum at a discrete time, the Behavior must therefore be a discrete-valued function. &amp;nbsp;In particular, there is a conflict inherent in overloading the meaning of a Behavior to both respond to Events and be continuous in time.&lt;br /&gt;&lt;br /&gt;Furthermore, I don't think the programmer loses anything by the reactive-banana model. &amp;nbsp;It's still possible to write functions that are continuous in time and lift them to Behaviors. &amp;nbsp;This is because the reactive-banana framework &lt;i&gt;has no notion of time&lt;/i&gt;. &amp;nbsp;If you want a time value, you need to provide one yourself, perhaps via a hardware clock.&lt;br /&gt;&lt;pre class="source-code"&gt;&lt;code&gt;&lt;br /&gt;-- a pure, continuous function&lt;br /&gt;-- using UTCTime to conveniently tie in with getTime.&lt;br /&gt;createShape :: UTCTime -&amp;gt; Shape&lt;br /&gt;&lt;br /&gt;-- an event source tied to the window toolkit's redraw event&lt;br /&gt;redrawScreen :: Event ()&lt;br /&gt;&lt;br /&gt;-- function to draw a shape to the screen&lt;br /&gt;drawShape :: Shape -&amp;gt; IO ()&lt;br /&gt;&lt;br /&gt;getTime :: IO UTCTime&lt;br /&gt;getTime = Data.Time.Clock.getCurrentTime&lt;br /&gt;&lt;br /&gt;reactiveSystem :: Prepare ()&lt;br /&gt;reactiveSystem =&lt;br /&gt; reactimate $ fmap drawShape&lt;br /&gt;                   (apply (pure createShape) (mapIO getTime redrawScreen))&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Let's examine how this works. &amp;nbsp;Fundamentally, when the window system requests a screen redraw, we want to check the current time, generate the correct shape for the time, and finally update the display. &amp;nbsp;Our system provides the redraw requests via an &lt;code&gt;Event ()&lt;/code&gt;, which is mapped to the current time, creating an &lt;code&gt;Event UTCTime&lt;/code&gt;. &amp;nbsp;The &lt;code&gt;createShape&lt;/code&gt; function is first lifted to a &lt;code&gt;Behavior&lt;/code&gt; then applied to the &lt;code&gt;Event UTCTime&lt;/code&gt;, creating an &lt;code&gt;Event Shape&lt;/code&gt;. &amp;nbsp;Finally the &lt;code&gt;drawShape&lt;/code&gt; function is mapped over this, creating an Event (IO ()) which can be &lt;code&gt;reactimate&lt;/code&gt;'d, i.e. each &lt;code&gt;IO ()&lt;/code&gt; is performed as it becomes available. &amp;nbsp;In my opinion, by removing time from the reactive-banana framework, both the described systems and the implementation are greatly simplified, with no loss of expressiveness.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Simultaneitys&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;One aspect that seems to cause a lot of difficulty is&amp;nbsp;&lt;a href="http://apfelmus.nfshost.com/blog/2011/03/31-frp-trouble-with-simultaneity.html"&gt;simultaneous events&lt;/a&gt;, multiple Events which occur at the same time. &amp;nbsp;There are a few ways these can arise, such as long sampling periods, merging Event streams, and recursive definitions. &amp;nbsp;reactive-banana specifies that the order of simultaneous events is undefined, providing the orderedDuplicate function to deal with certain cases where timing is important.&lt;br /&gt;&lt;br /&gt;I don't like this solution. &amp;nbsp;Semantically, it would be nice that an event stream were strictly increasing in time, disallowing simultaneous events entirely. &amp;nbsp;I suspect that this could be made usable by providing the following two primitive functions:&lt;br /&gt;&lt;pre class="source-code"&gt;&lt;code&gt;&lt;br /&gt;mergeCombine :: (a -&amp;gt; a -&amp;gt; a) -&amp;gt; Event a -&amp;gt; Event a -&amp;gt; Event a&lt;br /&gt;&lt;br /&gt;-- When two events arrive simultaneously, create two output events with the right event delayed slightly&lt;br /&gt;mergeOrdered :: Event a -&amp;gt; Event a -&amp;gt; Event a&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;code&gt;mergeOrdered&lt;/code&gt; function introduces a slight delay to preserve discrete events, whereas &lt;code&gt;mergeCombine&lt;/code&gt; has a function to combine two events to one. &amp;nbsp;&lt;code&gt;mergeCombine&lt;/code&gt; handles the common cases of dropping one event via &lt;code&gt;const&lt;/code&gt; and combining with &lt;code&gt;mappend&lt;/code&gt;, as well as more specialized functions. &amp;nbsp;I think these functions, together with the guarantee that an &lt;code&gt;Event&lt;/code&gt; stream only produces single events, are sufficient for a workable system.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So that's my initial reaction to reactive-banana. &amp;nbsp;The library is quite minimal, but so far it's sufficiently expressive for every problem I've thought of. &amp;nbsp;The new semantics of Behavior seem conducive to developing a correct model of how the implementation works, thereby making it simpler for users to reason about performance and operations. &amp;nbsp;Requiring users to explicitly manage time may eventually become a burden, however it's a direction worth exploring. &amp;nbsp;For many problems user code doesn't depend upon time anyway.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-7121294298304953735?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/7121294298304953735/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2011/04/thoughts-on-frp.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/7121294298304953735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/7121294298304953735'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2011/04/thoughts-on-frp.html' title='Thoughts on FRP'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-941301259264969521</id><published>2011-02-03T18:14:00.006Z</published><updated>2011-02-04T01:34:47.337Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='csound'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='x-dsp'/><title type='text'>a better audio language</title><content type='html'>One of my frequent frustrations with audio programming languages is that they're relatively unsafe, at least when compared to languages with sophisticated type systems.  The problem is generally worsened with EDSLs.  Consider this example csound:&lt;pre&gt;&lt;br /&gt;  aNull delayr 2&lt;br /&gt;  atap  deltap 1&lt;br /&gt;        delayw asig&lt;br /&gt;&lt;/pre&gt;Most csound dsl's will provide 3 functions corresponding to delayr, deltap, and delayw, which are translated to the dsl in a very direct manner.  If the delayr is left out, the dsl code is translated to invalid csound, detected only by the csound compiler.  I find this frustrating because we can do better.&lt;br /&gt;&lt;br /&gt;What if the delayr generates a token, which is then required by all functions that want to use it?&lt;pre&gt;&lt;br /&gt; -- Haskell-mode now&lt;br /&gt; myDelay asig = do&lt;br /&gt;   (dtok, _) &lt;- delayr 2     adelay &lt;- deltap dtok 1     delayw dtok asig     return adelay &lt;/pre&gt;This is better, but it's still possible to accidentally omit a delayw from the Haskell code.  Again, this is undetected except by the csound compiler at the final stage.  What we really need is a way to ensure that a user can run delays freely within the context of a delay line, but only in that context.  In Haskell, computations within contexts are generally represented by applicative functors.  Let's try that approach:&lt;pre&gt;&lt;br /&gt; -- don't export the constructor&lt;br /&gt; newtype DelayCtxt a = DelayCtxt { unDelay :: CsoundInterp a }&lt;br /&gt;&lt;br /&gt; deltap' :: CsoundInterp KSig -&gt; DelayCtxt ASig&lt;br /&gt; deltap' dtime = DelayCtxt $ dtime &gt;&gt;= deltap&lt;br /&gt;&lt;br /&gt; runDelay :: CsoundInterp ASig -&gt; CsoundInterp Float -&gt; DelayCtxt a -&gt; CsoundInterp a&lt;br /&gt; runDelay mAsig mtime delcomp = do&lt;br /&gt;   maxdel &lt;- mtime     asig &lt;- mAsig     delayr maxdel     rval &lt;- unDelay delcomp     delayw asig     return rval &lt;/pre&gt;This is more like it.  Since the DelayCtxt constructor isn't exported, the only way to create a DelayCtxt value is with deltap'.  The only way to consume a DelayCtxt is with runDelay, which automatically adds the required delayr and delayw.&lt;br /&gt;&lt;br /&gt;Unfortunately the user still needs to manually add the maximum delay time, but that's unavoidable if you're going to support varying delay times.  Consider that the input signal could be coming from a controller in real-time; to statically guarantee it's under a given bound would require a bounded signal type, which would be equivalent to this static delay bound anyway.&lt;br /&gt;&lt;br /&gt;This is the motivating example behind &lt;a href="http://tanimoto.us/%7Ejwlato/papers/XDSP_SEAMUS2011.pdf"&gt;X-DSP&lt;/a&gt;, which was presented at the 2011 SEAMUS conference at the University of Miami.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-941301259264969521?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/941301259264969521/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2011/02/better-audio-language.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/941301259264969521'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/941301259264969521'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2011/02/better-audio-language.html' title='a better audio language'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-4770873606615781674</id><published>2010-12-14T18:04:00.005Z</published><updated>2010-12-14T19:35:49.253Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='gtk2hs'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenGL'/><title type='text'>installing gtkglext Haskell bindings with gtk-osx</title><content type='html'>I just managed to install the Haskell bindings to gtkglext with the quartz-based gtk-osx, and I want to record the necessary steps so I don't forget.  This assumes you have a working &lt;a href="http://haskell.org/ghc/"&gt;ghc&lt;/a&gt; (ghc-7), &lt;a href="http://gtk-osx.sourceforge.net/"&gt;gtk-osx&lt;/a&gt;, and XCode. As a quick overview, I needed to do the following:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;install gtkglext (c library)&lt;/li&gt;&lt;li&gt;install gtk (Haskell package)&lt;/li&gt;&lt;li&gt;install gtkglext (Haskell package)&lt;/li&gt;&lt;/ol&gt;1.  Install gtkglext (c)&lt;br /&gt;&lt;br /&gt;I downloaded the "Quartz hack full patched source" and followed the instructions at &lt;a href="http://answerpot.com/showthread.php?324502-GtkGLExt+OS+X+Quartz+hack+patch"&gt;http://answerpot.com/showthread.php?324502-GtkGLExt+OS+X+Quartz+hack+patch&lt;/a&gt;.  There are a few problems:&lt;br /&gt;The file "docs/reference/gtkglext/html/index.sgml" doesn't exist.&lt;br /&gt;The headers aren't installed.&lt;br /&gt;Several references are made to undefined constants.  Unfortunately this error doesn't show up until much later, when GHC tries to link every symbol under the sun.&lt;br /&gt;&lt;pre&gt;jhbuild shell&lt;br /&gt;tar -xf gtkglext-1.2.0.osx-hack.tar&lt;br /&gt;cd gtkglext-1.2.0&lt;br /&gt;patch -p1 &amp;LT gtkglext_c.patch&lt;br /&gt;./configure &amp;amp;&amp;amp; make&lt;br /&gt;touch docs/reference/gtkglext/html/index.sgml&lt;br /&gt;sudo make install&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;2.  Install gtk (Haskell)&lt;br /&gt;&lt;br /&gt;The Haskell package "gtk" currently (as of gtk-0.12.0) doesn't build with a quartz-based gtk+.  I downloaded the source ("cabal unpack") and edited the file "Graphics/UI/Gtk/General/Structs.hsc" to disable the drawableGetID function.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;jhbuild shell&lt;br /&gt;cabal unpack gtk&lt;br /&gt;cd gtk&lt;br /&gt;patch -p1 &amp;LT gtk.patch&lt;br /&gt;cabal install&lt;/pre&gt;If you installed the gtkglext libraries to a standard folder this should be all that's necessary.  If cabal can't find gdkglext-quartz or gtkglext-quartz, you'll need to add the library paths at this stage.&lt;br /&gt;&lt;br /&gt;3.  Install gtkglext (Haskell package)&lt;br /&gt;&lt;br /&gt;This was painful.&lt;br /&gt;&lt;br /&gt;There are a few different problems to deal with:&lt;br /&gt;1.  The gtkglext install didn't install the headers, so we need to specify them manually.&lt;br /&gt;2.  We're not using pkg-config, so we edit the gtkglext.cabal file, remove the pkg-config check, and specify the libraries to link to.&lt;br /&gt;3.  Several symbols are undefined in gtkglext, so we need to remove references to them from the Haskell source.  The undefined symbols are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;  gdk_gl_config_get_screen&lt;/li&gt;&lt;li&gt;  gdk_gl_config_get_depth&lt;/li&gt;&lt;li&gt;  gdk_gl_context_get_gl_config&lt;/li&gt;&lt;li&gt;  gdk_gl_context_get_share_list&lt;/li&gt;&lt;li&gt;  gdk_gl_context_is_direct&lt;/li&gt;&lt;li&gt;  gdk_gl_context_get_render_type&lt;/li&gt;&lt;/ul&gt;AFAICT these were never defined in gtkglext, although they're listed in the headers.&lt;br /&gt;&lt;br /&gt;There's also another argument to glWindowNew, which we need to add.&lt;br /&gt;&lt;br /&gt;Finally, the demo program needs to be patched, otherwise it just sits there and doesn't update.&lt;br /&gt;&lt;br /&gt;So execute the following, replacing PATH_TO_GTKGLEXT with wherever you unpacked/built gtkglext:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;jhbuild shell&lt;br /&gt;cabal unpack gtkglext&lt;br /&gt;cd gtkglext-0.12.0&lt;br /&gt;patch -p1 &amp;LT gtkglext.patch&lt;br /&gt;cabal install --extra-include-dir="PATH_TO_GTKGLEXT" --extra-include-dir="PATH_TO_GTKGLEXT/gdk" --extra-include-dir="$PREFIX/include/cairo/" --extra-include-dir="$PREFIX/include/glib-2.0/" --extra-include-dir="$PREFIX/lib/glib-2.0/include/" --extra-include-dir="$PREFIX/include/gtk-2.0/" --extra-include-dir="$PREFIX/include/pango-1.0/" --extra-include-dir="$PREFIX/include/atk-1.0/" --extra-include-dir="$PREFIX/lib/gtk-2.0/include/"&lt;br /&gt;cd demo&lt;br /&gt;make&lt;br /&gt;./RotatingCube&lt;/pre&gt;And you should get a new window with a colorful cube, spinning in full OpenGL glory.&lt;br /&gt;&lt;br /&gt;I don't know why it's necessary to specify all the gtk+ header locations  manually here, when they're automatically found for building gtk.&lt;br /&gt;&lt;br /&gt;The patch files can be downloaded from &lt;a href="http://tanimoto.us/~jwlato/gtk2hs/"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-4770873606615781674?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/4770873606615781674/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2010/12/installing-gtkglext-haskell-bindings.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/4770873606615781674'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/4770873606615781674'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2010/12/installing-gtkglext-haskell-bindings.html' title='installing gtkglext Haskell bindings with gtk-osx'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-6871152256773226973</id><published>2009-12-09T23:55:00.004Z</published><updated>2009-12-10T01:35:07.832Z</updated><title type='text'>*rant* - Microsoft, why do you do this to me?</title><content type='html'>(Recent discussions of the Windows platform have led me to disclose my opinion of a particular infelicity of ASP.Net)&lt;br /&gt;&lt;br /&gt;Background: a few years ago, my day job consisted primarily of web development using ASP.Net v.2 and C#.  Overall I quite enjoyed the experience.  C# is IMHO a pretty decent language, and .NET is alright too.  Everything works perfectly smoothly, until you travel off the beaten path.&lt;br /&gt;&lt;br /&gt;If Perl's maxim is "There's more than one way to do it," then ASP.Net's is "There's more than one way to do it, but the likelihood of any given way working properly is inversely proportional to the number of ways to do it."&lt;br /&gt;&lt;br /&gt;I had an interface, let's call it IHasData.  This interface provided a function to pretty-print information associated with different types of events for a web calendar.  So far so good.  I wanted to display several different events in a GridView.  "Simple," I said to myself, "just put them in a generic collection and assign the data source."  So I did that, with code pretty much like this (apologies if the syntax is off; I haven't done C# for years):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;GridView gv = new GridView();&lt;br /&gt;ListSet&lt;IHasData&gt; events = new ListSet&lt;IHasData&gt;();&lt;br /&gt;&lt;br /&gt;//code to fill the events set omitted.&lt;br /&gt;&lt;br /&gt;gv.DataSource = events;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and everything should work like a charm.  Except, now my page sometimes threw an exception.&lt;br /&gt;&lt;br /&gt;It was an odd exception too, indicating that some particular IHasData instance didn't have an unrelated method (i.e. not exposed by IHasData) that wasn't supposed to be in that class anyway.  My code was only using IHasData methods; the types ensured that nothing else could be accessed.  So why was .Net trying to load something unrelated?&lt;br /&gt;&lt;br /&gt;The actual problem was pretty clear.  .Net was using reflection on the first element of the data source, and assuming that all remaining elements would have exactly the same type.  .Net's reflection facilities are quite powerful, and once they're in play all members of a class are available, regardless of the type used to access it.  Not necessarily a good idea for a generic list on interfaces.  I have no idea why the GridView has this behavior, but it makes me sad.  Particularly since it could have been done right and been that much more useful.&lt;br /&gt;&lt;br /&gt;Ultimately this was only one of a host of problems that stemmed from trying to use NHibernate, persistent objects, and .Net built-in features like the ObjectDataSource.  It would have been really slick if it worked as advertised, but there were some significant design flaws and the implementation was half-baked at best.  I did eventually create a framework that filled most of the gaps, but I was  making up for functionality the framework was supposed to provide and didn't.  Not good.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-6871152256773226973?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/6871152256773226973/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2009/12/rant-microsoft-why-do-you-do-this-to-me.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/6871152256773226973'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/6871152256773226973'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2009/12/rant-microsoft-why-do-you-do-this-to-me.html' title='*rant* - Microsoft, why do you do this to me?'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-4138063528025821128</id><published>2009-11-15T23:44:00.002Z</published><updated>2009-11-16T00:02:11.739Z</updated><title type='text'>iteratee status</title><content type='html'>I haven't posted for a while, so I wanted to provide a small update.  I've been working on the following updates:&lt;br /&gt;&lt;br /&gt;1)  Providing integrated pure and monadic iteratees. I've just implemented an iteratee class, but I don't yet know how performance compares to the original version.  I'm also considering a GADT version, but I suspect it'll look ugly.&lt;br /&gt;&lt;br /&gt;2)  Exception and error handling.  Haven't done much work, but I've been thinking about this and have some ideas to implement.  Strings are not sufficient.&lt;br /&gt;&lt;br /&gt;3)  Reorganizing the modules.  Most of this is done, although to some extent the final version will depend upon results of item 1.&lt;br /&gt;&lt;br /&gt;4)  Performance.  I've been developing a benchmark suite utilizing Criterion.  The ultimate goal is to use this suite to compare performance of multiple versions of the library.&lt;br /&gt;&lt;br /&gt;5)  Parsec integration.  &lt;a href="http://inmachina.net/~jwlato/haskell/ParsecIteratee.hs"&gt;This&lt;/a&gt; resulted from a user request.  It's useful enough that I'll probably integrate it into the main distribution.&lt;br /&gt;&lt;br /&gt;Also, there's a &lt;a href="http://trac.haskell.org/iteratee/"&gt;trac instance&lt;/a&gt; available.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-4138063528025821128?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/4138063528025821128/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2009/11/iteratee-status.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/4138063528025821128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/4138063528025821128'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2009/11/iteratee-status.html' title='iteratee status'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-4577793464346851392</id><published>2009-08-12T12:16:00.003+01:00</published><updated>2009-08-12T13:16:50.448+01:00</updated><title type='text'>Restricted-element containers and maps</title><content type='html'>Given the perspective we have now, I think it's a little hard to imagine how ground-breaking the concept of type classes were when they were first introduced in Haskell.  I think it says a lot that, after the Haskell Committee decided upon their inclusion, they made type classes an integral part of the language.  Lest you think that's an exaggeration, can you imagine what Haskell would like like today without, e.g. Num, Functor, or Monad?&lt;br /&gt;&lt;br /&gt;As a consequence of using a new idea, some areas that Haskell (and Haskellers!) currently struggle with are related to type classes.  One area in particular are containers and type classes.  A "Holy Grail" of sorts for Haskellers is the creation of a container type class that provides all the useful container functions.  Ideally, all useful container types would be able to be instances of this class.&lt;br /&gt;&lt;br /&gt;One package that provides this sort of functionality is John Goerzon's excellent ListLike package,&lt;a href="http://hackage.haskell.org/package/ListLike"&gt; http://hackage.haskell.org/package/ListLike&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;ListLike provides (in addition to many others), the functions "map" and "rigidMap".  Why two?  The difference is in the types:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&gt; class ListLike full item | full -&gt; item where&lt;br /&gt;&gt;   map :: ListLike full' item' =&gt; (item -&gt; item') -&gt; full -&gt; full'&lt;br /&gt;&gt;   rigidMap :: (item -&gt; item) -&gt; full -&gt; full&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The standard map (and Functor's fmap) allow mapping from a container of any element type to a container of any other element type.  rigidMap requires that both the input and output elements and containers have the same type.  This is very useful for monomorphic containers.   If you provide an instance for ByteString Word8, you can't use the native 'map' because it only maps over elements of the same type since Word8 is the only valid element type.  ListLike's default 'map' function builds a new container by destructing and reconstructing the old one.  This is highly inefficient compared to native maps, but is the only way to write the function that satisfies the type signature.  Even though "ListLike ByteString Word8" is the only instance that can exist, there's no way to prove it to the compiler.  'rigidMap', by further restricting the types, can use the native map, and is therefore much more efficient when it can be used.&lt;br /&gt;&lt;br /&gt;Unfortunately there is another case, one that I expect will become more common as certain high-performance libraries become more widespread.  Many interesting containers are polymorphic but require class contexts on their elements.  Examples are the UVector and StorableVector packages, which require elements to be instances of UA and Storable respectively.&lt;br /&gt;&lt;br /&gt;Here's the problem.  Using StorableVector as an example, these packages often provide the following map:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&gt; map :: (Storable item, Storable item') =&gt; (item -&gt; item') -&gt; Vector item -&gt; Vector item'&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;How can we use this in ListLike?  It could certainly be used directly for 'rigidMap', but what about 'map'?  Unfortunately that isn't possible because of the class restriction on the output element type.  The type variable for that element isn't mentioned in the class definition (i.e. it doesn't appear in the instance head).  Since it's not in the instance head, it isn't possible to put a class context on it.  If you fall back to the default 'map' instance any possible efficiency gains are lost.&lt;br /&gt;&lt;br /&gt;One solution is to include the output item type in the class definition, but that would require users to put an extra type variable in every time the class context is used, which is a waste for something that is only used for one function.&lt;br /&gt;&lt;br /&gt;I think the correct approach is to create a new type class, call it LooseMap, as follows:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&gt;  class (ListLike full item, ListLike full' item') =&gt; LooseMap full item full' item' where&lt;br /&gt;&gt;    looseMap :: (item -&gt; item') -&gt; full -&gt; full'&lt;br /&gt;&gt;&lt;br /&gt;&gt; --StorableVector instance&lt;br /&gt;&gt;  instance (Storable el, Storable el') =&gt; LooseMap (Vector el) el (Vector el') el' where&lt;br /&gt;&gt;    looseMap = Data.StorableVector.map&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Of course completely polymorphic containers (e.g. List) could easily be made instances of this class as well.  I would think that "map" should be completely supplanted by "looseMap", although rigidMap would still be necessary for monomorphic types.&lt;br /&gt;&lt;br /&gt;As a side note, there is another possible solution.  I experimented with using type families to encode Functors (and thus a polymorphic map) as RFunctors, using the same approach as in the RMonad package.  This worked fine for map, but I couldn't figure out how to write a fold that satisfied the typechecker.  I think it's impossible, and would love to hear from anyone who knows how to make it work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-4577793464346851392?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/4577793464346851392/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2009/08/restricted-element-containers-and-maps.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/4577793464346851392'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/4577793464346851392'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2009/08/restricted-element-containers-and-maps.html' title='Restricted-element containers and maps'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-6757265520904578879</id><published>2009-04-19T05:57:00.003+01:00</published><updated>2009-04-19T06:03:38.627+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Music'/><category scheme='http://www.blogger.com/atom/ns#' term='Random'/><title type='text'>SEAMUS recap</title><content type='html'>The 2009 SEAMUS Conference is over, and the finale featured the coolest thing ever.  A video of an earlier concert is on Youtube already, of course (I didn't see the most recent concert posted yet).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=aOZEpP_zzaw&amp;amp;feature=related"&gt;http://www.youtube.com/watch?v=aOZEpP_zzaw&amp;amp;feature=related&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-6757265520904578879?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/6757265520904578879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2009/04/seamus-recap.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/6757265520904578879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/6757265520904578879'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2009/04/seamus-recap.html' title='SEAMUS recap'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-1006448628467037341</id><published>2009-03-12T02:44:00.003Z</published><updated>2009-04-07T13:15:28.653+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Iteratee'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><title type='text'>future directions for iteratee</title><content type='html'>I have recently completed two major projects that were taking up nearly all of my time.  Now that they are done, I'd like to work on the next version of the iteratee package.  Here are a few ideas that I hope to include soon.&lt;br /&gt;&lt;br /&gt;1.  Remove the explicit Maybes&lt;br /&gt;This was suggested by Oleg, and it seems to be a good idea.  Every iteratee that produces a value other than () uses Maybe to represent the possibility of failure (always an option when doing IO).  By incorparating the Maybe into the type of IterateeG, these will no longer need to be explicit.  This will also make IterateeGM an instance of MonadZero to the extent that Maybe is an instance of that class.&lt;br /&gt;&lt;br /&gt;Status: I've made a patch that does this, but it doesn't yet work properly with convStream.  I haven't managed to track down the problem.  Likely to be included, assuming I can solve this issue in a reasonable time frame.&lt;br /&gt;&lt;br /&gt;2.  Stream type class (and simplify StreamChunk)&lt;br /&gt;I keep hoping for a ListLike type class to enter common use.  Barring that, I have some ideas for breaking up the necessary functions of StreamChunk into separate type classes, such as the patch to use a monoid instance submitted by Bas van Dijk.&lt;br /&gt;&lt;br /&gt;Status: Changes will be made, it is likely that StreamChunk will be broken into multiple smaller classes.  Any addition of a Stream type class will wait until after point 4 is resolved.&lt;br /&gt;&lt;br /&gt;3.  More utility iteratees (foldl, filter, others?)&lt;br /&gt;Status: Likely to be included.  Changes to StreamChunk will make these easier to support.&lt;br /&gt;&lt;br /&gt;4.  Type-safe seeking&lt;br /&gt;If iteratees are parameterized by Stream, the type of the stream should indicate if seeking is supported.  I have an outline for how to implement this, but haven't done any work yet.&lt;br /&gt;&lt;br /&gt;Status: Needs research, this will probably wait for the next major version bump.&lt;br /&gt;&lt;br /&gt;5.  Improved error handling&lt;br /&gt;Bas van Dijk submitted a patch to change the type of a stream error from String to Error a.  Others have suggested other possible changes as well.&lt;br /&gt;&lt;br /&gt;Status: Needs more research, this will likely wait for the next major version bump.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-1006448628467037341?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/1006448628467037341/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2009/03/future-directions-for-iteratee.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/1006448628467037341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/1006448628467037341'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2009/03/future-directions-for-iteratee.html' title='future directions for iteratee'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-4274373613990048932</id><published>2009-03-12T00:22:00.009Z</published><updated>2009-03-12T02:44:30.782Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Iteratee'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='IO'/><title type='text'>enumerator/iteratees and output</title><content type='html'>I have recently received a few questions about writing output while using enumerator-based I/O.  In some cases users have attempted to make enumerators (like enumFd) that will handle output as well, but have difficulty actually making it work.&lt;br /&gt;&lt;br /&gt;I think these problems stem from an incorrect application of the enumerator model of I/O.  When using enumerators, a file (or other data source) is a resource that can be enumerated over to process data, exactly as a list can be enumerated over in order to access the data contained in the list.  Compare the following:&lt;br /&gt;&lt;br /&gt;foldl f init xs&lt;br /&gt;&lt;br /&gt;enumFd "SomeFile" ==&lt;&lt; stream2list&lt;br /&gt;&lt;br /&gt;In the first function, 'xs' is the data to be processed, 'foldl' tells how to access individual items in the data collection, and 'f' and 'init' do the actual processing.  In the second, "SomeFile" is the data, 'enumFd' tells how to access the data, and 'stream2list' does the processing.  So how does writing fit in?  The output file obviously isn't the data source, and it doesn't make sense to enumerate over your output file as there's no data there to process.  So it must go within the Iteratee.  It turns out that making an iteratee to write data is relatively simple:  &lt;pre&gt;&lt;br /&gt;&gt; import Data.Iteratee&lt;br /&gt;&gt; import System.IO&lt;br /&gt;&gt; import Control.Monad&lt;br /&gt;&gt;&lt;br /&gt;&gt; writeOut :: FilePath -&gt; IterateeGM [] Char IO ()&lt;br /&gt;&gt; writeOut file = do&lt;br /&gt;&gt;  h &lt;- liftIO $ openFile file WriteMode&lt;br /&gt;&gt;  loop h&lt;br /&gt;&gt;  where&lt;br /&gt;&gt;  loop :: Handle -&gt; IterateeGM [] Char IO ()&lt;br /&gt;&gt;  loop h = do&lt;br /&gt;&gt;   next &lt;- Data.Iteratee.head&lt;br /&gt;&gt;   case next of&lt;br /&gt;&gt;    Just c -&gt; liftIO $ hPutChar h c &gt;&gt; loop&lt;br /&gt;&gt;    Nothing -&gt; liftIO $ hClose h&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Add some error handling and you've got a writer.  This version could be polymorphic over different StreamChunk instances by generalizing the type (FlexibleContexts may be required as well).  Other stream-specific versions could be written that would take advantage of the specific StreamChunk instance (e.g. using Data.ByteString.hPut instead of hPutChar).&lt;br /&gt;&lt;br /&gt;I hope this will serve as a very basic introduction to output when using enumerators.  In addition to a generic writer like this, it may frequently be beneficial to define special-purpose writers.  In a future post I will show a writer that seeks within the output file using a threaded State monad.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-4274373613990048932?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/4274373613990048932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2009/03/enumeratoriteratees-and-output.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/4274373613990048932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/4274373613990048932'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2009/03/enumeratoriteratees-and-output.html' title='enumerator/iteratees and output'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-1078023605129596129</id><published>2009-02-03T00:50:00.002Z</published><updated>2009-02-03T01:56:53.829Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='IO'/><title type='text'>Build a better WAVE reader, pt 2</title><content type='html'>In the last post, I looked at the beginnings of using an Iteratee to create a WAVE file reader.  I achieved a runtime of 25 seconds, compared to 1.9 seconds for my benchmark code.&lt;br /&gt;&lt;br /&gt;Oleg's &lt;a href="http://okmij.org/ftp/Haskell/Iteratee/"&gt;Iteratee&lt;/a&gt; code uses a Stream type which internally represents data as Chunk [a].  Haskell lists have many great properties, but they are not particularly efficient for this type of numerical processing.  There are many better options, including &lt;a href="http://www.haskell.org/ghc/docs/latest/html/libraries/array/Data-Array.html"&gt;Arrays&lt;/a&gt;, &lt;a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/uvector"&gt;UVectors&lt;/a&gt;, and &lt;a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/storablevector"&gt;StorableVectors&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Unfortunately all of these options I would want to use introduce a type class restriction on types of elements in the array.  This isn't a problem for sound files in particular, although it does greatly limit the utility of the Iteratees in general.  One of the Iteratee examples shows how Iteratees can be layered for text processing, using IterateeGM String m a iteratees layered on IterateeGM Char m a iteratees to provide operations on words in a text stream.  This is no longer possible, as String is not an instance of the type classes necessary to use one of these more compact data structures.  In the end, the performance benefits should be worth it.&lt;br /&gt;&lt;br /&gt;I chose StorableVector to replace the basic list, because it seemed handy while I was experimenting.  StorableVector turns out to have another good property which is key to achieving the goal of performance close to C.&lt;br /&gt;&lt;br /&gt;Adapting the Iteratees to use StorableVector is straightforward.  Unfortunately when we've finished, performance is actually worse!  In fact, it's as bad as the original RBIO-based version.  It turns out that one key function is the culprit: conv_stream.&lt;br /&gt;&lt;br /&gt;conv_stream converts between two streams.  It is capable of converting multiple elements from one stream to one (or no) elements of the other, using a user-supplied conversion function.  The wave file reader uses conv_stream to convert from a stream of Word8's to a stream of Doubles so the user can operate on Doubles, with the library handling all conversions transparently.   The original type of conv_stream is:&lt;br /&gt;&lt;blockquote&gt;conv_stream :: Monad m =&gt;&lt;br /&gt;                            IterateeGM el m (Maybe [el']) -&gt; EnumeratorN el el' m a&lt;/blockquote&gt;Unfortunately, this means our data needs to unpacked from the StorableVector Word8 into separate Word8's, those Word8's assembled into Just [Double], and finally packed into a StorableVector Double for the next Iteratee.  This is very inefficient, but easily fixed.  Just change the type of conv_stream to&lt;br /&gt;&lt;blockquote&gt;conv_stream :: (Storable el, Storable el', Monad m) =&gt;&lt;br /&gt;  IterateeGM el m (Maybe (Vec.Vector el')) -&gt; EnumeratorN el el' m a&lt;/blockquote&gt;&lt;br /&gt;and we're done.  In fact, the implementation doesn't change at all from the original, just the type.  Now no packing or unpacking need take place.&lt;br /&gt;&lt;br /&gt;With the new conv_stream, it's necessary to change the conversion function.  This is the IterateeGM which converts Word8's into Doubles.  I have actually split this code into two parts, a function (called unroller because the original list-based version was a manually-unrolled endian_read) which converts Word8s into Word16 or Word32 as necessary, and a normalization function to normalize the result and convert from Words to Double.  The normalization function should be fine as is, all that's necessary to change is the unroller function.  Ideally, it would operate on StorableVectors directly.  Fortunately this is possible.  All that's necessary is to get a ForeignPtr Word8 from a StorableVector, and cast it to ForeignPtr Wordn.  A somewhat hackish 16-bit converter can be implemented as follows:&lt;br /&gt;&lt;blockquote&gt;import qualified Foreign.Ptr as FP&lt;br /&gt;import qualified Foreign.ForeignPtr as FFP&lt;br /&gt;import qualified Data.StorableVector as Vec&lt;br /&gt;&lt;br /&gt;unroll_16 :: (Monad m) =&gt; IterateeGM Word8 m (Maybe (Vec.Vector Word16))&lt;br /&gt;unroll_16 = liftI $ IE_cont step&lt;br /&gt;  where&lt;br /&gt;  step (Chunk vec)&lt;br /&gt;    | Vec.null vec                               = unroll_16&lt;br /&gt;    | Vec.length vec == 1                  = liftI $ IE_cont $ step' vec&lt;br /&gt;    | Vec.length vec `rem` 2 == 0 = liftI $ IE_done (convert_vec vec) (Chunk $ Vec.empty)&lt;br /&gt;    | True                                            = let (h, t) = Vec.splitAt (Vec.length vec - 1) vec&lt;br /&gt;                                                               in&lt;br /&gt;                                                               liftI $ IE_done (convert_vec h) (Chunk t)&lt;br /&gt;  step stream                                    = liftI $ IE_done Nothing stream&lt;br /&gt;  step' i (Chunk vec)&lt;br /&gt;    | Vec.null vec                               = liftI $ IE_cont $ step' i&lt;br /&gt;    | Vec.length vec `rem` 2 == 1  = let vec' = Vec.append i vec&lt;br /&gt;                                                               in&lt;br /&gt;                                                               liftI $ IE_done (convert_vec vec') (Chunk $ Vec.empty)&lt;br /&gt;    | True                                             = let (h, t) = Vec.splitAt (Vec.length vec - 1) vec&lt;br /&gt;                                                                in&lt;br /&gt;                                                                liftI $ IE_done (convert_vec $ Vec.append i h) (Chunk t)&lt;br /&gt;  step' _i stream                                = liftI $ IE_done Nothing stream&lt;br /&gt;  convert_vec vec = let (fp, off, len) = VB.toForeignPtr vec&lt;br /&gt;                                         f = FP.plusPtr (FFP.unsafeForeignPtrToPtr fp) off&lt;br /&gt;                                         fp' = (FFP.castForeignPtr $ unsafePerformIO $ FFP.newForeignPtr_ f) :: FFP.ForeignPtr Word16&lt;br /&gt;                                    in&lt;br /&gt;                                    Just $ VB.fromForeignPtr fp' (len `div` 2)&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;and that's it!  The big hack is that this only works on little-endian platforms.  To be correct, the bytes would need to be swapped on big-endian systems only.  Implementing a suitable swap function is currently an excercise for the reader.  Note that unsafeForeignPtrToPtr should be safe as there is no finalizer associated with the ForeignPtr, and in any case the memory pointed to won't be GC'd until after the Iteratee is complete.&lt;br /&gt;&lt;br /&gt;At this point, the data should be handled entirely as a StorableVector, with no conversions to or from any intermediate lists at any point.  Here are results from running the test:&lt;br /&gt;&lt;blockquote&gt;$ time ./test_iter_sb&lt;br /&gt;Just (AudioFormat {numberOfChannels = 2, sampleRate = 44100, bitDepth = 16})&lt;br /&gt;Just 0.977568895535142&lt;br /&gt;&lt;br /&gt;real    0m1.661s&lt;br /&gt;user    0m1.335s&lt;br /&gt;sys     0m0.140s&lt;/blockquote&gt;And the proof is in the pudding.  This is the fastest Haskell implementation yet, even faster than hsndfile in a lazy stream.&lt;br /&gt;&lt;br /&gt;A bit of work remains to be done.  It should be cleaned up, with the hack fixed properly.  Also I've currently only implemented unroll_16.  For 24-bit audio, I expect the creation of a Word24 type and Storable instance will be necessary in order to employ the same technique.  However, I think I've proven that a pure-Haskell, high performance audio I/O library is possible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-1078023605129596129?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/1078023605129596129/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2009/02/build-better-wave-reader-pt-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/1078023605129596129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/1078023605129596129'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2009/02/build-better-wave-reader-pt-2.html' title='Build a better WAVE reader, pt 2'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-5075379994072233377</id><published>2009-02-02T02:11:00.009Z</published><updated>2009-02-03T00:53:56.393Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='IO'/><title type='text'>Build a better WAVE reader, pt 1</title><content type='html'>I have recently been working on creating an optimal audio I/O library in Haskell.  By optimal, I mean the library should do all of the following:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Have a clean, functional, usable interface.   Operations should be composable, and the user should not have to deal with recursion, folds or the like.  Procedural-style interfaces (anything exposing filehandle-type operations to the user) are out too.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Be space efficient.  Some Haskell implementations suffer from space leaks, or can have space leaks if the user isn't careful with data.&lt;/li&gt;&lt;li&gt;Have performance comparable to hsndfile, a Haskell binding to libsndfile.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Be implemented purely in Haskell.  This makes it interesting.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;The standard for audio I/O performance in Haskell has been &lt;a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/hsndfile"&gt;hsndfile&lt;/a&gt;, as was discussed on &lt;a href="http://www.mail-archive.com/haskell-art@lurk.org/msg00047.html"&gt;haskell-cafe&lt;/a&gt; not long ago.  Testing shows that using hsndfile to read data in a lazy stream fashion (similar to lazy ByteStrings) is about 10 times faster than the next-fastest implementation.  Not good for Haskell's claim of having speed comparable to C!  Still, I'm convinced it's possible to do better.&lt;br /&gt;&lt;br /&gt;My test case consists of finding the peak normalized amplitude of a 16-bit stereo WAVE file.  The audio duration is about 6 minutes, with a total file size of about 66 MB.  This task involves reading and processing the entire file.  The processing is quite minimal, so the speed of an implementation should depend primarily on the efficiency of reading and normalizing audio data.&lt;br /&gt;&lt;br /&gt;Using hsndfile in a lazy stream does provide an interface which is familiar to many Haskell users, but I'm not particularly fond of it.  If the user is not careful, the entire file can be retained in memory after processing. For a large audio file, this leads to an unacceptable situation.  It should be impossible for data to be retained without explicit action from the user.  Also, dependencies on foreign libraries can be difficult for some (i.e. Windows) users to resolve.&lt;br /&gt;&lt;br /&gt;So having decided to create a native Haskell implementation, the first place to start is with the semantics and interface.  I recently read Oleg K.'s presentations on Iteratee processing in Haskell, and I'm convinced this is the best model for functional I/O.  It just feels right, what else can I say?  Read the &lt;a href="http://okmij.org/ftp/Haskell/Iteratee/DEFUN08-talk-notes.pdf"&gt;paper&lt;/a&gt;, see the &lt;a href="http://okmij.org/ftp/Haskell/Iteratee/"&gt;code&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A preliminary version of Iteratee-based processing was benchmarked as "Enumerator" in the Haskell-art discussion.  It was roughly comparable with other Haskell solutions, but has some problems.  Notably it doesn't support seeking within the file, which would be nice.  After I developed that, Oleg released a new version of Iteratee which does support seek operations via the RBIO monad.  So I dropped Enumerator in favor of the new Iteratee + RBIO.&lt;br /&gt;&lt;br /&gt;Oleg helpfully provides a TIFF reader with his Iteratee code.  I based my wave reader on that, using the same technique of storing Enumerators for each wave sub-chunk in an IntMap.  Since they aren't tagged, a list probably would have served just as well, but since most wave files only have 2 or 3 chunks anyway it doesn't seem like it matters much.  Changes here aren't going to have a large performance impact.&lt;br /&gt;&lt;br /&gt;The first version to use Iteratee + RBIO executes in about 45 seconds.  Ouch.&lt;br /&gt;&lt;br /&gt;RBIO uses IORef's to communicate seek requests and answers between Iteratees and Enumerators.  Unfortunately IORefs are slow, so I'll start by getting rid of them.  The data type&lt;br /&gt;&lt;blockquote&gt;data IterateeG el m a = IE_done a (StreamG el)&lt;br /&gt;                    | IE_cont (StreamG el -&gt; IterateeGM el m a)&lt;/blockquote&gt;&lt;br /&gt;can be changed to&lt;br /&gt;&lt;blockquote&gt;data IterateeG el m a = IE_done a (StreamG el)&lt;br /&gt;                    | IE_cont (StreamG el -&gt; IterateeGM el m a)&lt;br /&gt;                    | IE_seek FileOffset (StreamG el -&gt; IterateeGM el m a)&lt;/blockquote&gt;&lt;br /&gt;This also necessitates adding one more case to (&gt;&gt;=) on Iteratees:&lt;br /&gt;&lt;blockquote&gt;iter_bind m f = m &gt;&gt;== docase&lt;br /&gt;   where&lt;br /&gt;   docase (IE_done a (Chunk [])) = f a&lt;br /&gt;   docase (IE_done a stream) = f a &gt;&gt;== (\r -&gt; case r of&lt;br /&gt;                              IE_done x _  -&gt; liftI $ IE_done x stream&lt;br /&gt;                              IE_cont k       -&gt; k stream&lt;br /&gt;                              iter                   -&gt; liftI iter)&lt;br /&gt;   docase (IE_cont k) = liftI $ IE_cont ((&gt;&gt;= f) . k)&lt;br /&gt;   docase (IE_seek off k) = liftI $ IE_seek off ((&gt;&gt;= f) . k)&lt;/blockquote&gt;&lt;br /&gt;After these changes, the provided Iteratees can be adapted to use the new structure relatively easily.  The enumerator that reads from a file, enum_fd_random, must also be updated to understand seek requests.  At this point we can remove RBIO entirely, and our execution time is about 33 sec.  Adding a few carefully-chosen INLINE's and SPECIALIZATION's gets runtime down to 25 sec.  Better, but we're very far from the goal of being comparable with C.&lt;br /&gt;&lt;br /&gt;The next step is to change the Iteratee's internal representation of data.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-5075379994072233377?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/5075379994072233377/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2009/02/build-better-wave-reader.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/5075379994072233377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/5075379994072233377'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2009/02/build-better-wave-reader.html' title='Build a better WAVE reader, pt 1'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5981432593633653194.post-7655464807985597618</id><published>2009-02-02T01:57:00.000Z</published><updated>2009-02-02T02:11:25.703Z</updated><title type='text'>Introduction</title><content type='html'>Hi, and welcome.  I've decided to start this blog as a way to share some of my findings with various communities I'm involved with.  I will try to post when I have something important/interesting to say, so I may not update all that frequently.&lt;br /&gt;&lt;br /&gt;My main interests are music and programming.  Specifically, modern art music, electroacoustic music, and programming in order to serve some musical purpose.  For the moment, my language of choice is Haskell, because it's far more interesting than other languages I know.  If Perl is for people who want to Get Things Done, then Haskell is for people who want to Do Things Right, Eventually, After You Figure Out Exactly What That Means.  Which suits me just fine.&lt;br /&gt;&lt;br /&gt;So again, welcome and thanks for taking a look.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5981432593633653194-7655464807985597618?l=johnlato.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://johnlato.blogspot.com/feeds/7655464807985597618/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://johnlato.blogspot.com/2009/02/introduction.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/7655464807985597618'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5981432593633653194/posts/default/7655464807985597618'/><link rel='alternate' type='text/html' href='http://johnlato.blogspot.com/2009/02/introduction.html' title='Introduction'/><author><name>John Lato</name><uri>http://www.blogger.com/profile/14086288462709102194</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
