Hacker News new | ask | show | jobs
by bad_user 3842 days ago
I guess Rust's iterators are pretty standard, meaning that you keep calling "next(): T" until there are no more elements, right?

Well that's pretty cool and iterators are generic, but not generic enough. If you'll look closely their protocol has certain constraints. For example the next() call is synchronous. If the next element is not ready, well, tough luck, the current thread needs to be blocked. Another constraint is that iterators produce elements, right? Well, iterators are cool and useful, but many operators like map(), filter() and flatMap() can be applied on things that don't necessarily produce values. As a side note, this is why monads are hard to understand, being a little more abstract than iterators.

What transducers from Clojure are doing is to define a more generic protocol (more generic than that of Iterator) such that you can have defined operators, like map and filter, operate on whatever you want, such that you can reuse the implementation of those operators. You can also compose those operators, before applying them to something concrete.

2 comments

On further thought, the genericism of transducers seems to hurt when you want to combine multiple streams, even somewhat trivially:

   zip_flat xs ys = zip (flatten xs) (flatten ys)
I don't see how you could implement this as a transducer, since a transducer isn't able to reason about its inputs.
> Well, iterators are cool and useful, but many operators like map(), filter() and flatMap() can be applied on things that don't necessarily produce values.

You've lost me. You need to apply the function in `flatMap` to something, and `flatMap` by definition will output zero or more values.

So what do you mean?

The signature of flatMap is something like this:

    flatMap[M,A,B](cc: M[A], f: A => M[B]): M[B]
So given that you respect its signature, M[A] is not necessarily a producer of values. It can be anything and it helps if you think about it not like a collection or a container, but more like a context. For example M[A] could be a function of type S => (A, S). And then you could map and flatMap on such functions to produce other functions. This is the "state monad" btw.