Let\'s say I\'m creating a data pipeline which will process text files. I have the following types and functions:
data A = A deriving (Show, Typeable)
data B
I may be misunderstanding your problem, but why not simply wrap the functions up with a name?
It seems a very straightforward approach to the situation which doesn't require a lot of messing about.
module NamedFunctions where
import Control.Category -- so I can generalise (.)
import Prelude hiding ((.),id) -- don't worry, available from Category
Store a list of function names you've composed along with the function itself:
data Fn name a b = Fn {names::[name],apply:: (a -> b)}
When you show a function, just show what you composed to get there:
instance Show name => Show (Fn name a b) where
show f = show $ names f
Define new versions of (.)
and ($)
:
infixr 9 ...
(...) :: Fn name b c -> Fn name a b -> Fn name a c
f ... g = Fn (names f ++ names g) (apply f . apply g)
infixr 0 $$$
($$$) :: Fn name a b -> a -> b
f $$$ x = apply f x
and reclaim (.)
to work on named functions
instance Category (Fn name) where
(.) = (...)
id = Fn [] id
You can leave functions you don't care about unnamed
so they don't contribute to the names
list.
name n f = Fn [n] f
unnamed f = Fn [] f
You can use whatever data you like to name each function, for example:
timesThree = name "(*3)" (*3)
addFour = name "(+4)" (+4)
step1 :: Fn Int A B
step1 = name 1 undefined
step2 :: Fn Int B C
step2 = name 2 undefined
step3 :: Fn Int C D
step3 = name 3 undefined
*NamedFunctions> timesThree . addFour
["(*3)","(+4)"]
*NamedFunctions> timesThree . addFour $$$ 5
27
*NamedFunctions> step3.step2.step1
[3,2,1]
*NamedFunctions> step3.step2.step1 $$$ A
*** Exception: Prelude.undefined