muesli4 / table-layout

Layout data in grids and pretty tables. Provides a lot of tools to get the cell formatting right (positional alignment, alignment on specific characters and limiting of cell width)..
BSD 3-Clause "New" or "Revised" License
38 stars 11 forks source link

Increase flexibility to specify rows #13

Open muesli4 opened 4 years ago

muesli4 commented 4 years ago

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:

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.