
takeC :: Int -> Compoz a b -> (exists c. Compoz a c) dropC :: Int -> Compoz a b -> (exists c. Compoz c b)
What does 'exists' means? To create a rank-2 type can't you use:
takeC :: Int -> Compoz a b -> (forall c. Compoz a c)
??
2011/2/28 Heinrich Apfelmus
Yitzchak Gale wrote:
You have written a large software system in Haskell. Wishing to play to Haskell's strength, you have structured your system as a series of composable layers. So you have data types
Layer1, Layer2, ...
and functions
layer2 :: Layer1 -> Layer2 layer3 :: Layer2 -> Layer3 ....
etc.
Of course you'll want to be able to run any range of the layers separately, at least for testing and debugging purposes if not for the functionality itself.
So your UI module (command line or whatever) that launches your application provides a data type
data Layers = Layers Int Int
that indicates which layers to run, and functions
deserialize1 :: L.ByteString -> Layer1 deserialize2 :: L.ByteString -> Layer2 ....
serialize1 :: Layer1 -> L.ByteString serialize2 :: Layer2 -> L.ByteString ....
etc.
Now you need a function
runLayers :: Layers -> L.ByteString -> L.ByteString
so that the effect is for example
runLayers (Layers 4 6) = serialize6 . layer6 . layer5 . deserialize4
[..]
What is the best way to write runLayers? Feel free to change the details of the above design, as long as it meets the functionality requirements expressed.
Solution: compose all the functions, but do not use the standard function composition (.) to do that. Instead, make a new data type with composition as constructor. This way, you can inspect the composition afterwards and run only parts of it.
Solution, put differently: Make a type-safe list of the whole chain of functions. Then, the runLayers function throws away everything outside the range and composes what is left.
Here a rough sketch of what I have in mind:
data Compoz a b where Id :: Compoz a a Cons :: (Serialize a,b,c) => (b -> c) -> Compoz a b -> Compoz a c
-- this value needs to be written out chain = layer20 `Cons` layer 19 ...
runLayers (Layer a b) = deserialize . (run . takeC (b-a) . dropC a $ chain) . serialize
takeC :: Int -> Compoz a b -> (exists c. Compoz a c) dropC :: Int -> Compoz a b -> (exists c. Compoz c b)
run :: Compoz a b -> (a -> b)
Of course, you will have to wrestle with the existential types for takeC and dropC a bit, but that shouldn't be much of a problem. For instance, you can fuse these functions into runLayers and hide the existential types somewhere in the recursion.
Regards, Heinrich Apfelmus
-- http://apfelmus.nfshost.com
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe