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!)
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.
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.
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.
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?
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.
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-streamis implemented and works. (Which I find to be pretty interesting!)Haskell streaming is pretty simple. It looks something like
Here
mis some kind of monadic action. If you have a StreamT then calling next on it gives you anm-action which produces either a terminal value (of typer) or an intermediate value (of typea) and a new stream. You can inject pure listsand 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)
And you can also do something like run an effect for each value in the stream
Anyway, this is all just the beginning. These ideas are much more deeply developed in
pipes.Thanks tel!
They’re also developed much further in
conduitandmachineswhich 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.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.
Any chance you can provide some info about what problems
scalaz-streamhas? In my looking atscalaz-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?
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.