module Zinza.Type (
    Ty (..),
    tyUnit,
    displayTy,
    ) where

import qualified Data.Map as M

import Zinza.Var

-- $setup
-- >>> import Zinza
-- >>> import Data.Proxy (Proxy (..))

-- | Zinza types.
--
-- The 'selector's tell how the Haskell value can be
-- converted to primitive value. E.g.
--
-- >>> toType (Proxy :: Proxy Char)
-- TyString (Just "return")
--
-- Here, 'return' converts 'Char' to 'String'.
--
data Ty
    = TyBool                                 -- ^ boolean
    | TyString (Maybe Selector)              -- ^ string
    | TyList (Maybe Selector) Ty             -- ^ lists
    | TyRecord (M.Map Var (Selector, Ty))    -- ^ records
    | TyFun Ty Ty                            -- ^ functions
  deriving (Eq, Ord, Show)

-- | A record without fields is a unit type. Think of zero-field tuple: @()@.
tyUnit :: Ty
tyUnit = TyRecord M.empty

-- | Pretty print 'Ty'.
displayTy :: Ty -> String
displayTy ty = go 0 ty "" where
    go :: Int -> Ty -> ShowS
    go _ TyBool       = showString "Bool"
    go _ (TyString _) = showString "String"
    go _ (TyList _ t) = showChar '[' . go 0 t . showChar ']'
    go _ (TyRecord m) = case M.toList m of
        []            -> showString "{}"
        ((n,(_,t)) : nts) -> foldl
            (\acc (n',(_,t')) -> acc . showString ", " . showPair n' t')
            (showChar '{' . showPair n t)
            nts
            . showChar '}'
    go d (TyFun a b) = showParen (d > 10) $
        go 11 a . showString " -> " . go 10 b

    showPair n t = showString n . showString ": " . go 0 t