Tuesday, June 19, 2007

Fun with Functions

Pete Nuttall writes about the fnreduce function:

fnreduce :: [(a->a)]->a->a
fnreduce [] value = value
fnreduce (a:as) value = freduce as $ a value


A shorter, although not necessarily clearer, expression of this function would be

fnreduce as value = (foldr (flip (.)) id as) value


This just says that to apply a list of functions to a value, we first compose all the functions by folding down the list, then apply the result to the value. The important thing here is (flip (.)), which says to compose backwards; it means that fnreduce [f, g, h] returns (h . g . f . id) instead of (f . g . h . id). An interesting side note is that it doesn't matter (from a semantic point of view) whether I use foldl or foldr, since function composition is associative:

f . (g . (h . id)) = ((id . f) . g) . h


But "flip" here feels a little confusing. A clearer approach would be to reverse the list instead of reversing the operator:

fnreduce as value = foldr (.) id (reverse as) value


This might be less efficient, but it says in a much clearer way what I'm doing.

Yet another option is to eschew the use of function composition:

fnreduce as value = foldl (flip ($)) value as


Here ($) is the application function; (f$x) applies f to x. So if we reverse it (call this $$), we get (x$$f) applies f to x. Folding this from the left down the list [f, g, h] gives us (((value$$f)$$g)$$h), which is exactly what the original fnreduce did.

I think this is maybe the best fold-based solution to the original problem, because it models what's happening (repeatedly transforming a value with a series of functions) directly. To use a right-fold, you have to remove the flip and reverse as (the reason this is necessary is left as an exercise to the reader).




Pete also asks whether this can be done for heterogenous lists in a type-safe manner. The core problem is, what is the type of our list? It can't actually be a list: in order to check that it's OK to combine the output of one function with the input of another, we would need the elements of the list to have different types. You can't do this in Haskell, even a crazy ghc-extended Haskell.

So, we could try something like this:

module Test where

data TransformList a b where
Nil :: TransformList a a
Cons :: (a -> b) -> TransformList b c -> TransformList a c

apply :: TransformList a b -> a -> b
apply Nil = id
apply (Cons f fs) = (apply fs) . f


This lets us do stuff like

> apply (Cons (\x -> x + 1) (Cons (\x -> show x) Nil)) 5
"6"


This is cute, but I don't know that it's really that useful. After all, how is it any better than the following?

> ((\x -> show x) . (\x -> x + 1)) 5
"6"


Everything the above code lets you do with TransformList could just as well be done with the humble compose operator. The only benefit would be if we could somehow do "list-like" stuff with a transform list. For instance, say we want to write the following function:


traceIntermediates Nil x = (x, [])
traceIntermediates (Cons f l) x = let x' = f x
(x'', ss) = traceIntermediates l x'
in
(x'', (show x'):ss)


This won't compile, because we don't know that x' is a member of the Show typeclass. And there is no way to fix this in a reusable way using any technique that relies on building a type parameterized on the input and output types: the problem is that we need to say something about the types of the intermediate values, but their type variables are inaccessible.

In other words, there isn't any way to write down a constraint on the type "TransformList a b" that constrains the intermediates. So I've just wasted a lot of typing duplicating the compose operator. Yay.

On the other hand, it's entirely possible to do this if you don't mind some boilerplate code.

data ShowTransformList a b where
Nil :: (Show a) => ShowTransformList a a
Cons :: (Show a, Show b, Show c) => (a -> b) -> ShowTransformList b c -> ShowTransformList a c

traceIntermediates :: ShowTransformList a b -> a -> (b, [String])
traceIntermediates Nil x = (x, [])
traceIntermediates (Cons f l) x = let x' = f x
(x'', ss) = traceIntermediates l x'
in
(x'', (show x'):ss)

apply :: ShowTransformList a b -> a -> b
apply Nil = id
apply (Cons f fs) = (apply fs) . f


Now I can do this:

> traceIntermediates
(Cons (\x -> x + 1)
(Cons (\x -> x * 2)
(Cons (\x -> x - 5)
(Cons show
(Cons (read :: String -> Double)
Nil))))) 5
(7.0,["6","12","7","\"7\"","7.0"])

2 Comments:

At 8:26 PM, Blogger oakleyses said...

uggs outlet, uggs on sale, ray ban sunglasses, ray ban sunglasses, louis vuitton, michael kors outlet online, oakley sunglasses wholesale, christian louboutin outlet, louis vuitton, uggs outlet, louis vuitton outlet, polo outlet, prada handbags, nike free, chanel handbags, longchamp outlet, michael kors outlet, replica watches, louis vuitton outlet, oakley sunglasses, michael kors outlet online, prada outlet, michael kors outlet online, longchamp outlet, burberry handbags, michael kors outlet, kate spade outlet, ray ban sunglasses, longchamp outlet, louis vuitton outlet, oakley sunglasses, nike air max, oakley sunglasses, replica watches, ugg boots, polo ralph lauren outlet online, ugg boots, gucci handbags, jordan shoes, cheap oakley sunglasses, michael kors outlet online, christian louboutin uk, burberry outlet, tory burch outlet, tiffany and co, christian louboutin shoes

 
At 8:33 PM, Blogger oakleyses said...

doudoune moncler, pandora uk, moncler outlet, vans, converse outlet, montre pas cher, louis vuitton, moncler, moncler, canada goose, canada goose outlet, ugg uk, links of london, barbour uk, supra shoes, replica watches, lancel, nike air max, moncler, moncler, moncler outlet, coach outlet, wedding dresses, canada goose outlet, pandora jewelry, karen millen uk, ugg, marc jacobs, juicy couture outlet, converse, moncler uk, louis vuitton, ugg pas cher, swarovski, pandora jewelry, gucci, canada goose, canada goose uk, ugg,uggs,uggs canada, pandora charms, juicy couture outlet, louis vuitton, louis vuitton, ray ban, ugg,ugg australia,ugg italia, canada goose jackets, swarovski crystal, canada goose, hollister, thomas sabo, canada goose outlet, toms shoes

 

Post a Comment

<< Home