I don't understand why these things aren't all unified in the first place, it's just function composition we're talking about here right?
Edit: I mean it's clearly not just simple function composition, but I don't understand why not
(def xform (comp (map inc) (filter even?))) ; xform is a transducer
(defn xform [aseq] (->> aseq (map inc) (filter even?))) ; xform is a fn
Why do these have to be different things? Why is there a need for machinery to do what fn composition is supposed to do?(And if you say "deferring an action is just another term for 'functions'" then that's exactly the point, lifting more algorithmic logic into composable functions)
so Functors (fmap)? fmap still uses regular old fns, no machinery.
http://clojuredocs.org/clojure_contrib/clojure.contrib.gener... http://learnyouahaskell.com/making-our-own-types-and-typecla...
- reducers can work on tree structures, and thus can exploit parallelism. This would be like using a map that requires only the Foldable typeclass
- In Haskell you have stream/vector fusion, but it's not obvious to know when ghc will actually exploit it, you might want to use something like Control.Monad.Stream or Data.Vector. In theory it might be generalized to all Foldables, but in practice for now it might be a good enough compromise to stick to a limited number of types (the one that support such transducers)
So: nothing terribly new/groundbreaking, but it might bring something like stream fusion to the masses (of clojure developers :P )
type Iteratee el m a -- a processor of 'els' in the monad 'm' returning a type 'a'
type Enumerator el m a = Iteratee el m a -> m (Iteratee el m a)
The Enumerators library is complicated by the presence of monads and by trying to automatically close handles when the stream is processed. In some ways it seems that the goal of solving the lazy IO problem led to missing a much simpler abstraction. Transducers seem to be simpler and more focused on just abstracting stream/map-reduce computation.We called the bits in the middle "step functions" , which could be combined with "consumers", "producers" and "transformers".
And the algebra generalizes hugely (not just collections) but to things like concurrent processes, data flow programs etc.
http://metagraph.org/papers/stream_fusion.pdf
Things to think about in a non-Haskell settings: how do you prevent reordering side-effects? Can execeptions/non-termination being reordered be observed?
Is it correct to say that the relationship between the new forms of map/filter/etc, reduction functions, and transducers is something like this:
"traditional" (map f l) is equivalent to (foldl (mapR f) l []), where (mapR f) is the "reduction function" corresponding to map. (mapR would still be list specific)
the new (map f) is a transducer that takes a reduction function and returns another reduction function; given idR, the "identity reduction function" for foldl such that (foldl idR l []) = l, ((map f) idR) = mapR.
Furthermore, given reduction functions mapRR such that (foldr (mapRR f) l []) == (map f l) and idRR such that (foldr idRR l []) = l, then ((map f) idRR) = mapRR. (Because all the list-specific things in the output reduction function come from the input reduction function, the transducer (map f) doesn't need to know anything about lists, so can be used in other contexts as well -- one minor-but-perhaps-easier-to-illustrate aspect of that is, even when used with lists, (map f) is independent of the folding direction, unlike the reduction functions mapR and mapRR.)
(I know this is barely even scratching the surface of the applications, but I'm trying to confirm that I've got the concept right.)
map f: (a->b)->(x->b->x)->(x->a->x)
filter pred: (a->bool)->(x->a->x)->(x->a->x)
flatmap f: (a->[b])->(x->b->x)->(x->a->x)
etc.
transduceMap :: (b -> a) -> (acc -> a -> acc) -> (acc -> b -> acc)
transduceMap f = \reduceFn -> \acc el -> reduceFn acc (f el)
lambda added for clarity (no pun intended), however types are easier to match when using this syntax: transduceMap :: (b -> a) -> (acc -> a -> acc) -> acc -> b -> acc
transduceMap f reduceFn acc el = reduceFn acc (f el)For clarity, let's define a type alias for reducers:
type Reducer[X, A] = (X, A) ⇒ X
Let's define `map` to match the type definition you provided. And with that type definition, I only see one way in which the function can be implemented. So it must be: def map[X, A, B](f: A ⇒ B): (Reducer[X, B] ⇒ Reducer[X, A]) =
(redB: Reducer[X, B]) ⇒ (x: X, a: A) ⇒ redB(x, f(a))
How can I use this? Let's try the following: def addup(zero: Int, a: List[Int]) = a.foldLeft(zero)(_ + _)
def parseList(a: List[String]) = a.map(_.toInt)
map(parseList)(addup)(1, List("7", "8"))
This returns 16. OK, parsing the list, and adding up starting from 1. But it doesn't look to me like `map` implements anything like the usual semantic of map. It just converts the data structure, and applies the reducer. What am I missing here?