lol-typing-1.20160822: Language/LOL/Typing/Type/Monotype.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Language.LOL.Typing.Type.Monotype where
import Data.Bool
import Data.Either (Either(..), either)
import Data.Eq (Eq(..))
import Data.Function (($), (.), id)
import Data.Functor (Functor(..), (<$>))
import Data.Int (Int)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Maybe (Maybe(..), maybe, isNothing)
import Data.Monoid (Monoid(..), (<>))
import Data.Ord (Ord(..))
import Data.String (IsString(..))
import Data.Text (Text)
import Data.Text.Buildable (Buildable(..))
import Prelude (Num(..))
import Text.Read (read)
import Text.Show (Show(..))
import qualified Data.Char as Char
import qualified Data.Foldable as Foldable
import qualified Data.List as List
import qualified Data.Text as Text
import qualified Language.LOL.Typing.Lib.Data.Text.Buildable as Build
-- * Type 'Monotype'
-- | A /monomorphic type/ (aka. /monotype/).
--
-- NOTE: all 'Monotype's that can be constructed are NOT necessarily well-formed.
--
-- NOTE: however, a useful typing of this type language
-- is added by separating 'Monotype', 'Polytype' and 'Polytyref'
-- in order to indicate where to expect a 'Polyvar' or a 'Polytyref_Var'.
data Monotype
= Monotype_App Monotype Monotype -- ^ /binary type application/
| Monotype_Const Monoconst -- ^ /type constant/
| Monotype_Var Monovar -- ^ /type variable/
deriving (Eq, Ord, Show)
instance Buildable Monotype where
build = build . (precedence_Toplevel,)
instance Buildable (Precedence, Monotype) where
build (prec, typ)
| prec >= precedence typ = Build.parens (go typ)
| otherwise = go typ
where
go ty =
case app_spine_left ty of
Monotype_Var v `App_Spine` [] -> "m" <> build v
Monotype_Const c `App_Spine` [] -> build c
Monotype_Const "->" `App_Spine` [t1, t2] ->
build (precedence_Fun, t1) <>
" -> " <>
build (precedence_previous precedence_Fun, t2)
Monotype_Const "[]" `App_Spine` [t1] ->
"[" <> build t1 <> "]"
Monotype_Const (is_Tuple -> True) `App_Spine` tys ->
Build.tuple (build <$> tys)
t `App_Spine` tys ->
mconcat $ List.intersperse " " $
(build . (precedence_App,)) <$> (t : tys)
instance IsString Monotype where
fromString = Monotype_Const . fromString
-- * Type 'Precedence'
-- | A /binding precedence/ for an operator.
newtype Precedence
= Precedence Int
deriving (Eq, Ord, Show)
-- ** Class 'Has_Precedence'
class Has_Precedence a where
precedence :: a -> Precedence
instance Has_Precedence Monotype where
precedence ty =
case app_spine_left ty of
Monotype_Const "->" `App_Spine` [_, _] -> precedence_Fun
Monotype_Const "[]" `App_Spine` [_] -> precedence_Atomic
Monotype_Const (is_Tuple -> True) `App_Spine` _ -> precedence_Atomic
_ `App_Spine` [] -> precedence_Atomic
_ -> precedence_App
precedence_previous :: Precedence -> Precedence
precedence_previous (Precedence p) = Precedence (p - 1)
-- ** Convenient 'Precedence's
precedence_Toplevel :: Precedence
precedence_Toplevel = Precedence 0
precedence_Fun :: Precedence
precedence_Fun = Precedence 1
precedence_App :: Precedence
precedence_App = Precedence 2
precedence_Atomic :: Precedence
precedence_Atomic = Precedence 3
-- ** Type 'Monoconst'
-- | A /type constant/.
type Monoconst = Text
-- ** Type 'Monoconsts'
-- | A context of 'Monoconst's, without duplicates.
type Monoconsts = Map Monoconst ()
-- *** Class 'Has_Monoconsts'
-- | Return the 'Monoconst's of a 'Monotype', without duplicates.
class Has_Monoconsts a where
monoconsts :: a -> Monoconsts
instance Has_Monoconsts Monotype where
monoconsts ty =
case ty of
Monotype_Var _ -> Map.empty
Monotype_Const c -> Map.singleton c ()
Monotype_App t1 t2 -> monoconsts t1 `Map.union` monoconsts t2
{-
-- | Return the 'Monoconst's of a 'Has_Monotypes' instance, without duplicates.
monoconsts_from :: Has_Monotypes a => a -> [Monoconst]
monoconsts_from =
List.nub . List.concatMap go . monotypes
where
go (Monotype_Var _) = []
go (Monotype_Const c) = [c]
go (Monotype_App t1 t2) = go t1 `List.union` go t2
-}
-- | Infinite list of unique 'Monoconst's:
-- @a, b, .., z, a1, b1 .., z1, a2, ..@
const_pool :: [Monoconst]
const_pool =
[ Text.singleton n
| n <- ['a'..'z']
] <>
[ Text.pack (n:show i)
| n <- ['a'..'z']
, i <- [1 :: Int ..]
]
-- | Return given 'Monoconst' renamed a bit to avoid
-- conflicting with any given 'Monoconst's.
const_freshify
:: Monoconsts
-> Monoconst
-> (Monoconsts, Monoconst)
const_freshify consts_used const =
let ints = [1..] :: [Int] in
let fresh_const =
List.head
[ x
| extra <- "" : (show <$> ints)
, x <- [const <> Text.pack extra]
, isNothing (Map.lookup x consts_used)
] in
( Map.insert fresh_const () consts_used
, fresh_const )
-- | Return given 'Monotype'
-- with all its 'Monotype_Var's
-- turned into 'Monotype_Const's.
--
-- NOTE: each 'Monovar' being mapped to a 'Monoconst'
-- prefixing by an underscore ('_')
-- the 'show'ed 'Int' of the 'Monovar'.
constify :: Monotype -> Monotype
constify ty =
case ty of
Monotype_Var v -> Monotype_Const $ Text.pack ('_':show v)
Monotype_Const s -> Monotype_Const s
Monotype_App l r -> Monotype_App (constify l) (constify r)
-- | Return given 'Monotype'
-- with all previously 'constify'ed 'Monovar's
-- turned back into 'Monotype_Var's.
unconstify :: Monotype -> Monotype
unconstify ty =
case ty of
Monotype_Var v -> Monotype_Var v
Monotype_Const (Text.uncons -> Just ('_', c))
| not (Text.null c)
&& Text.all Char.isDigit c ->
Monotype_Var (read $ Text.unpack c)
Monotype_Const c -> Monotype_Const c
Monotype_App l r -> Monotype_App
(unconstify l)
(unconstify r)
-- *** Useful 'Monoconst's
type_Bool :: Monotype
type_Bool = Monotype_Const "Bool"
type_Char :: Monotype
type_Char = Monotype_Const "Char"
type_Float :: Monotype
type_Float = Monotype_Const "Float"
type_Int :: Monotype
type_Int = Monotype_Const "Int"
type_String :: Monotype
type_String = Monotype_Const "String"
-- | Constructs a function 'Monotype' from one 'Monotype' to another.
type_Fun :: Monotype -> Monotype -> Monotype
type_Fun t1 = Monotype_App (Monotype_App (Monotype_Const "->") t1)
-- | Right associative alias for 'type_Fun'.
(.->.) :: Monotype -> Monotype -> Monotype
(.->.) = type_Fun
infixr 0 .->.
-- | For instance, @(type_List type_Int)@ represents @[Int]@
type_List :: Monotype -> Monotype
type_List = Monotype_App (Monotype_Const "[]")
-- | For instance, @(type_IO type_Bool)@ represents @(IO Bool)@
type_IO :: Monotype -> Monotype
type_IO = Monotype_App (Monotype_Const "IO")
-- | A carthesian product of zero or more 'Monotype'.
-- For instance @(type_Tuple [])@ represents @()@,
-- and @(type_Tuple [type_Char, type_String])@ represents @(Char, String)@.
type_Tuple :: [Monotype] -> Monotype
type_Tuple tys = List.foldl Monotype_App (Monotype_Const name) tys
where
name | Foldable.null tys = "()"
| otherwise = Text.pack $ "("<>List.replicate (List.length tys - 1) ','<>")"
-- | The unit type. A special instance of 'type_Tuple'.
type_Unit :: Monotype
type_Unit = type_Tuple []
-- ** Type 'Monovar'
-- | A /monomorphic type variable/:
-- a place-holder for a 'Monotype' that is not yet known,
-- but that become available at some time during 'Constraint' solving.
type Monovar = Int
-- | Return the list of 'Monovar's of a 'Monotype', without duplicates.
monovars :: Monotype -> [Monovar]
monovars ty =
case ty of
Monotype_Var v -> [v]
Monotype_Const _ -> []
Monotype_App t1 t2 -> monovars t1 `List.union` monovars t2
-- * Type 'App'
-- | A /binary 'Monotype' application/.
type App = Monotype -> Monotype -> Monotype
-- | Left associative alias for 'Monotype_App'.
(.!.) :: Monotype -> Monotype -> Monotype
(.!.) = Monotype_App
infixl 5 .!.
-- | 'Monotype_App'ly given 'Monotype's to given 'Monotype'.
monoapp :: Monotype -> [Monotype] -> Monotype
monoapp = List.foldl Monotype_App
-- ** Type 'App_Spine'
-- | A /application spine/ of a 'Monotype'.
data App_Spine
= App_Spine
{ app_spine_end :: Monotype
, app_spine :: [Monotype]
}
-- | Return the /left 'App_Spine'/ of a 'Monotype_App'.
--
-- EXAMPLE: if type @t@ is @Either Bool [Int]@,
-- then @app_spine_left t@ is @(Either, [Bool, [Int]])@.
app_spine_left :: Monotype -> App_Spine
app_spine_left = go []
where
go tys (Monotype_App t1 t2) = go (t2:tys) t1
go tys ty = App_Spine ty tys
-- | Return the /right 'App_Spine'/ of a 'Monotype'.
--
-- EXAMPLE: if type @t@ is @Int -> (Bool -> String)@,
-- then @app_spine_right t@ is @([Int, Bool], String)@.
app_spine_right :: Monotype -> App_Spine
app_spine_right = go []
where
go tys (Monotype_App (Monotype_App (Monotype_Const "->") t1) t2) = go (t1:tys) t2
go tys ty = App_Spine ty (List.reverse tys)
-- | Return the /right 'App_Spine'/ of a 'Monotype' upto a maximal length.
app_spine_right_upto :: Int -> Monotype -> App_Spine
app_spine_right_upto maxlen ty =
let a `App_Spine` as = app_spine_right ty in
let (bs, cs) = List.splitAt maxlen as in
List.foldr (.->.) a cs `App_Spine` bs
-- ** Type 'Arity'
-- | The /arity of a 'Monotype'/,
-- i.e. the total number of expected arguments of a 'Monotype'.
type Arity = Int
-- | Return the 'Arity' of a 'Monotype'.
type_arity :: Monotype -> Arity
type_arity = List.length . app_spine . app_spine_right
-- * 'Monotype' predicates
is_Var :: Monotype -> Bool
is_Var (Monotype_Var _) = True
is_Var _ = False
is_Const :: Monotype -> Bool
is_Const (Monotype_Const _) = True
is_Const _ = False
is_App :: Monotype -> Bool
is_App (Monotype_App _ _) = True
is_App _ = False
is_Fun :: Monotype -> Bool
is_Fun (Monotype_App (Monotype_App (Monotype_Const "->") _) _) = True
is_Fun _ = False
is_Tuple :: Monoconst -> Bool
is_Tuple (Text.uncons -> Just ('(', t))
| Text.null t = False
| otherwise = Text.all (',' ==) (Text.init t) && Text.last t == ')'
is_Tuple _ = False
is_IO :: Monotype -> Bool
is_IO (Monotype_App (Monotype_Const "IO") _) = True
is_IO _ = False
-- * Class 'Has_Monotypes'
class Has_Monotypes a where
monotypes :: a -> [Monotype]
monotypes_map :: (Monotype -> Monotype) -> a -> a
instance Has_Monotypes Monotype where
monotypes ty = [ty]
monotypes_map = ($)
instance Has_Monotypes a => Has_Monotypes [a] where
monotypes = List.concatMap monotypes
monotypes_map f = (monotypes_map f <$>)
instance Has_Monotypes a => Has_Monotypes (Maybe a) where
monotypes = maybe [] monotypes
monotypes_map = fmap . monotypes_map
instance (Has_Monotypes a, Has_Monotypes b) => Has_Monotypes (Either a b) where
monotypes = either monotypes monotypes
monotypes_map f = either (Left . monotypes_map f) (Right . monotypes_map f)
-- ** Class 'Monotypeable'
-- | A type class to convert something into a 'Monotype'
class Monotypeable a where
monotype :: a -> Monotype
instance Monotypeable Monotype where
monotype = id
instance Monotypeable Monovar where
monotype = Monotype_Var
instance Monotypeable Monoconst where
monotype = Monotype_Const