Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I don't understand why you use a "Moore-style State Machine". I don't understand too why all the typed transducers which are shown here use a restricted version of reducers (i.e. a step function of type `r -> a -> r`). Rich Hickey points out that transducers must deal with initialization and completion. At minute 42:26 of his talk, he even presents a complete picture of tranducers type. They are reducer transformers where a reducer is rather made of 3 pieces :

   data Reducer a r s = Reducer {
      init :: () -> r,
      step :: r -> a -> r,
      complete :: r -> s
   }
We may than write :

   type Transducer a b = (Reducer a r s) -> (Reducer b r s)
But I think that we miss the point doing that. A transducer should be able to lead to a new reducer which use a different type to accumulate inputs. This opens the way to state management and the implementation of transducer like `taking` or `partition`.

Leaving Haskell for OCaml I'm more comfortable with :

   type ('a, 'b, 'c) red = {
      init : unit -> 'b;
      step : 'b -> 'a -> 'b;
      comp : 'b -> 'c;
   }

   let taking n reducer =
     let init () = (0,reducer.init ()) in
     let step (i,acc) item = if (i<n) then (i+1,reducer.step acc item) else (n,acc) in
     let comp (i,acc) = reducer.comp acc in
    {
       init = init;
       step = step;
       comp = comp
    }
The type of taking is `int -> ('a, 'b, 'c) red -> ('a, int * 'b, 'c) red`. The former `b` accumulator state has been replaced by a pair `(int,b)` where a counter is used to track how much items have been taken so far. Yes this should be improved with early termination. But the key point I want to highlight is that wrapping all the transducers into a single general type may imply the use of some sort of existential type (to abstract away the accumulation type). This general type is furthermore useless for the system to check the compatibility of a reducers along a transducer chain.

---

Another point I would like to say is that if transducers are undoubtedly an important and effective mean to build efficient transformation chains, they doesn't deserve to be pushed forward of conventional transformers (map, filter, take, ...) to which we are used for long.

Based on collection definition as something whose content is reducible, we can define the 'map' collection transformation on the basis of 'mapping' reducer transformation

  type 'a col = {
    fold: 'b 'c. ('a, 'b, 'c) red -> 'c
  }
   
  let map f col =
  {
    fold = fun reducer ->  col.fold (mapping f reducer)
  }
So, we have the clarity of map/filter/reduce with the power of transducers.

https://github.com/didier-wenzek/odist is a project where I started to implement these ideas some months ago. This transducer discussion will undoubtedly energize me to work again on the topic !



> But I think that we miss the point doing that. A transducer should be able to lead to a new reducer which use a different type to accumulate inputs.

So, instead of

  type Transducer a b = (Reducer a r s) -> (Reducer b r s)
you have:

  type Transducer a b r r' = (Reducer a r s) -> (Reducer b r' s)
Or, most generally (adding in the ability to change not only the type of the accumulator but also the type of the final result):

  type Transducer a b r r' s s' = (Reducer a r s) -> (Reducer b r' s')




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: