Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is a transducer different from a partially applied function?

After reading this article on Clojure (http://blog.podsnap.com/ducers2.html) introducing transducers, I'm confused on what a transducer is. Is a partially applied map in Haskell, such as map (+1) a transducer? At first I thought this was a Clojure way of using partial application, but then the article goes on to implement it in Haskell with an explicit type. What use does it have in Haskell?

like image 700
Ramith Jayatilleka Avatar asked Oct 30 '14 13:10

Ramith Jayatilleka


People also ask

What is the function of a transducer?

Usually a transducer converts a signal in one form of energy to a signal in another. Transducers are often employed at the boundaries of automation, measurement, and control systems, where electrical signals are converted to and from other physical quantities (energy, force, torque, light, motion, position, etc.).

What is a transducer programming?

A transducer is a composable higher-order reducer. It takes a reducer as input, and returns another reducer. Transducers are: Composable using simple function composition.

Which of the following is not a transducer Mcq?

(b) amplifier Any device which converts energy from one form to another is called a transducer. Loudspeaker and microphone are transducers but not an amplifier.


1 Answers

In Clojure (map inc) is a transducer, but not in Haskell, because Haskell has to obey currying but an untyped Lisp can break that curry-by-default convention. The type of transducers is instead:

type Transducer a b = forall r . (b -> r -> r) -> (a -> r -> r) 

We say that the transducer 'turns a into b'. Yes, the a and the b seem "backwards" on the right hand side. The forall means that a Transducer must leave r as a general type variable but is totally allowed to specialize on a and b.

Let me reverse two of the arguments in foldr:

-- cf. with foldr :: (x -> r -> r) -> r -> [x] -> r ffoldr :: (x -> r -> r) -> [x] -> r -> r ffoldr = flip . foldr 

(we can also use foldl but it will tend to reverse things later). This means that a Transducer can be used to transform the first argument of ffoldr from x to y, so that we can instead process an [x] with a y -> r -> r using foldr. Transducers 'sit between' the inputs ([x], r) and the final processor (y, r) -> r.

I've flipped the second two arguments to emphasize also that, surprisingly, ffoldr :: Transducer [x] x. By using the symmetry of the arguments we also have a generic composition of transducers, which happens to be just function composition:

(.) :: Transducer a b -> Transducer b c -> Transducer a c 

(If you think it's cool that giving these forall r terms lets us reverse how you normally use ., you can do it arbitrarily via a technique called "continuation passing".)

For example, here is the filter transducer:

tfilter :: (a -> Bool) -> (a -> r -> r) -> a -> r -> r      -- or: (a -> Bool) -> Transducer a a tfilter predicate f a = if predicate a then f a else id 

This applies the reduction function f to a and r only if the predicate holds. There is also a mapping transducer:

tmap :: (a -> b) -> (b -> r -> r) -> a -> r -> r  tmap ba f a = f (ba a) 

This gives composable map/filter semantics for any 'transducable' type: one map/filter fn can work for multiple contexts.

The Transducer type has a cute isomorphism: it turns out that the foldr of a list forall r. (x -> r -> r) -> r -> r is perfectly equivalent to that list [x] (it is the "Church encoding" of that list), and therefore swapping the argument a to the very front of the transducer definition gives us the (IMO much easier to understand!) type type TransL a b = a -> [b]. And this is much easier to understand:

tl_map f = \a -> [f a] tl_filter predicate = \a -> if predicate a then [a] else [] 

To run these on a list, use concatMap... which happens to be just >>=! So you just write collection >>= transducer and you have the transduced collection. The meaning of TransL a b is then, "take each element of the original list of a, and give me 0 or more elements of type b to splice into my outgoing list." It filters by splicing 0 elements when the predicate doesn't work; it maps by yielding 1 output element for each input element; another operation tl_dupe = \a -> [a, a] is a transducer which duplicates the elements in a list, [1,2,3] >>= tl_dupe becomes [1,1,2,2,3,3].

Where foldr appears to be a Transducer [x] x it is now seen to be identical to id :: TransL [x] x which has a way of simply performing a concat operation in the middle of a computation; the identity function in this algebra is actually return = \a -> [a], also written (:[]). The only loss is that we can no longer use . to compose these, but in fact the same composition is provided in Control.Monad as the Kleisli composition operator >=>.

So long story short, transducers are functions a -> [b] cleverly transformed with a bit of Church encoding so that the Kleisli composition operator for these Kleisli arrows of the list monad, becomes simply (.).

like image 70
CR Drost Avatar answered Sep 22 '22 17:09

CR Drost