Thursday, June 10, 2010

Surfing the Tuple Pipeline

A situation came up where I needed to pipeline a tuple created by a List.partition to two different functions and then return the result as a tuple. I decided to create a some pipeline operators for tuples. On reading Real-World Functional Programming (RWFP), however, I found a solution involving a map function and the standard forward pipe operator (|>). It turns out that I like the RWFP approach better than mine, but the special tuple pipelines I came up with are not without interest, so I decided to post them here. (As always, presented "as-is" and without warranty or implied fitness of any kind; use at your own risk.)

First, here are a couple of map functions adapted from the example on page 148 of RWFP. Please excuse my quick-and-dirty naming conventions. For clarity in this example only, I named the map functions “map12” for “map one function onto a two-tuple” and “map22” for “map two functions onto a two-tuple.”

let map12 f (a,b) = ((f a),(f b))
let map22 f g (a,b) = ((f a),(g b))

Here are my pipeline operators. The (||>) works like (|> map12), while the second two act like (map22 |>). (||>>) takes tupled functions, while (||>>> takes curried functions.

let (||>) (a,b) f = ((f a),(f b))
let (||>>) (a,b) (f,g) = ((f a),(g b))
let (||>>>) (a,b) f g = ((f a),(g b))

And here is a small test. Each example partitions a list of integers into lists of even and odd integers, creates a list of the halves of the even integers and the doubles of the odd integers, then sums the two lists and returns the sums as a tuple. (This is probably a useless task, but it demonstrates the operation without as much fluff as the real-world example I was working on.)

let isEven n = (n%2)=0
let doubleIt n = n*2
let halveIt n = n/2
let nums = [0..19]
let a = 
  List.partition isEven nums
  |> map22 ( halveIt) ( doubleIt)
  |> map12 List.sum
let b = 
  List.partition isEven nums
  ||>> ( halveIt, doubleIt)
  ||> List.sum
let c = 
  List.partition isEven nums
  ||>>> ( halveIt) <| ( doubleIt)
  ||> List.sum

All are fairly readable; in general, I prefer the RWFP approach because I feel it better extends the language than does a custom operator. But it would probably be possible to find design situations where any of the three was appropriate.

No comments: