1. 11
  1.  

  2. 3

    While I wrote this about Dylan, I think that this is more generally applicable and tried to keep the Dylan-specific aspects to a minimum. I think that some of the same issues are just as relevant in the standard libraries of other languages, including C++, Python, Ruby, and so on.

    I’d love to hear how other languages have solved this sort of thing (better or worse).

    I wish I’d flagged this with the “ask” tag.

    I have other posts that will be in this series including one that I’m working on now which is a breakdown of how scalaz-stream is implemented and works. (Which I find to be pretty interesting!)

    1. 2

      Haskell streaming is pretty simple. It looks something like

      data StreamT a m r = StreamT { next :: m (Either (a, StreamT a m r) r) }
      

      Here m is some kind of monadic action. If you have a StreamT then calling next on it gives you an m-action which produces either a terminal value (of type r) or an intermediate value (of type a) and a new stream. You can inject pure lists

      each :: Monad m => [a] -> StreamT a m ()
      each [] = StreamT (return (Right ()))
      each (a:as) = StreamT (return (Left (a, each as)))
      

      and get them back out by sequencing all of the effects (assuming there are a finite number of them and you can afford to run everything right now)

      toList :: Monad m => StreamT a m () -> m [a]
      toList s = do
        x <- next s
        case x of
          Right () -> return []
          Left (a, s') -> do
            as <- toList s'  -- here we sequence the rest of the computation
            return (a:as)    -- prior to returning anything
      

      And you can also do something like run an effect for each value in the stream

      runS :: Monad m => (a -> m ()) -> StreamT a m r -> m r
      runS f s = do
        x <- next s
        case x of
          Right r -> return r
          Left (a, s') -> f a >> runS s'
      

      Anyway, this is all just the beginning. These ideas are much more deeply developed in pipes.

      1. 1

        Thanks tel!

        They’re also developed much further in conduit and machines which I mentioned in my post. The more advanced features that make this useful for handling incremental I/O for non-blocking sockets are one of my major interests in this.

      2. 2

        We use scalaz-stream a lot at work. There are many problems with it but it’s definitely the best option on the JVM. I think Haskell has much better options.

        1. 2

          Any chance you can provide some info about what problems scalaz-stream has? In my looking at scalaz-stream, I’ve been looking at the append4-final branch which seems much nicer than the currently released version.

          What options do you consider to be better in Haskell?

          1. 1

            The append4-final brach looks very good. I’ve previously had problems with scalaz-streams requiring me to throw exceptions to terminate, using strictness too much that we have accidentally gotten non-termination, using Nothing which ruins inference, wanting concurrency but getting non-termination after merging blocking streams.

            machines is the Haskell library that scalaz-streams is modeled after and it’s much more sensible, if even just because a lot of my problems have been transitively the JVM. I also think the pipes library is much more sensible.