content: {-# LANGUAGE FlexibleContexts,
               TypeFamilies,
               NoMonomorphismRestriction #-}
  import Data.List (intercalate)
  import Data.List.Split (splitOn)
  import Lens.Micro
  
  (<$$>) :: (Functor f1, Functor f2) =>
            (a -> b) -> f1 (f2 a) -> f1 (f2 b)
  (<$$>) = fmap . fmap
  
  ------------------------------------------------------------
  -- reading and writing
  
  newtype CSV = CSV { values :: [[String]] }
  
  readCSV :: String -> CSV
  readCSV = CSV . (splitOn "," <$$> lines)
  
  instance Show CSV where
    show = unlines . map (intercalate ",") . values
  
  ------------------------------------------------------------
  -- construction and combination
  
  mkColumn, mkRow :: [String] -> CSV
  (<||>), (<==>) :: CSV -> CSV -> CSV
  
  mkColumn lst = CSV $ sequence [lst]
  mkRow lst    = CSV [lst]
  
  CSV t1 <||> CSV t2 = CSV $ zipWith (++) t1 t2
  CSV t1 <==> CSV t2 = CSV $ t1 ++ t2
  
  ------------------------------------------------------------
  -- access and modification via lenses
  
  table = lens values (\csv t -> csv {values = t})
  row i = table . ix i . traverse
  col i = table . traverse . ix i
  item i j = table . ix i . ix j
  
  ------------------------------------------------------------
  
  sample = readCSV "C1, C2, C3, C4, C5\n\
                   \1,  5,  9,  13, 17\n\
                   \2,  6,  10, 14, 18\n\
                   \3,  7,  11, 15, 19\n\
                   \4,  8,  12, 16, 20"
  