table-layout
table-layout copied to clipboard
Increase flexibility to specify rows
At the moment, row types are lists of the same type. This is not necessary at all and may be providing too much flexibility and too little at the same time:
- The type does not encode the width of the data. Sometimes this is helpful as the number of columns can be dynamic but often this is not required.
- Different columns may have different types that are an instance of
Cell
.
It turns out that this can be solved with little effort:
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
import Data.Foldable
class Cell a where
buildCell :: a -> String
newtype Wrapped = Wrapped String
instance Cell String where
buildCell = id
instance Cell Wrapped where
buildCell (Wrapped s) = s
data AnyCell = forall c. Cell c => AnyCell c
class Row a where
toCellList :: a -> [AnyCell]
instance {-# OVERLAPPING #-} (Cell a, Cell b) => Row (a, b) where
toCellList (x, y) = [AnyCell x, AnyCell y]
instance (Cell a, Cell b, Cell c) => Row (a, b, c) where
toCellList (x, y, z) = [AnyCell x, AnyCell y, AnyCell z]
instance {-# OVERLAPPABLE #-} (Foldable f, Cell a) => Row (f a) where
toCellList xs = AnyCell <$> toList xs
str :: Row r => r -> String
str r = unwords $ map f $ toCellList r
where
f (AnyCell c) = buildCell c
main :: IO ()
main = do
test ("foo", Wrapped "bar", "baz")
test (Wrapped "foo", "bar")
test [Wrapped "x", Wrapped "y", Wrapped "z"]
where
test :: forall r. Row r => r -> IO ()
test = putStrLn . str
Alternatively, one could also provide an instance for HList
and use instances from tuple-hlist
to define those.