lol-typing-1.20160822: Language/LOL/Typing/Type/Class.hs
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
-- | /type class/.
--
-- __Ressources:__
--
-- * /Type classes: exploring the design space/,
-- Simon Peyton Jones, Mark Jones, Erik Meijer, 1997,
-- http://research.microsoft.com/en-us/um/people/simonpj/Papers/type-class-design-space/multi.ps.gz
--
-- * /Implementing, and Understanding Type Classes/,
-- Oleg Kiselyov, 2014,
-- http://okmij.org/ftp/Computation/typeclass.html
module Language.LOL.Typing.Type.Class where
import Control.Arrow (first)
import Control.Monad (mapM)
import Data.Bool
import Data.Either (Either(..))
import Data.Eq (Eq(..))
import qualified Data.Foldable as Foldable
import Data.Function (($), (.), flip)
import Data.Functor ((<$>))
import qualified Data.List as List
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (Maybe(..), maybe)
import Data.Monoid ((<>))
import Data.Text (Text)
import Data.Text.Buildable (Buildable(..))
import Text.Show (Show(..))
import Language.LOL.Typing.Type.Monotype
import Language.LOL.Typing.Type.Substitution (Substitutable(..))
import Language.LOL.Typing.Type.Synotype
import Language.LOL.Typing.Type.Unification
import Language.LOL.Typing.Type.Qualification
-- * Type 'Class'
data Class
= Class
{ class_supers :: [Class_Name] -- ^ /adjacent super classes/
, class_instances :: [Class_Instance] -- ^ /class instances/
} deriving (Eq, Show)
-- ** Type 'Class_Instance'
data Class_Instance
= Class_Instance
{ class_instance_head :: Class_Qualifier
, class_instance_context :: [Class_Qualifier]
} deriving (Eq, Show)
-- ** Type 'Class_Qualifier'
data Class_Qualifier
= Class_Qualifier
{ class_qualifier_name :: Class_Name
, class_qualifier_type :: Monotype
} deriving (Eq, Show)
-- | Right associative alias for 'Qualification'.
(.=>.) :: [Class_Qualifier] -> a -> Qualification [Class_Qualifier] a
(.=>.) = Qualification
infixr 2 .=>.
instance Buildable Class_Qualifier where
build (Class_Qualifier q ty) =
build q <> " " <> build ty
instance Substitutable Class_Qualifier where
subvars = subvars . class_qualifier_type
sub `substitute` p = p{class_qualifier_type =
sub `substitute` class_qualifier_type p}
instance Has_Monoconsts Class_Qualifier where
monoconsts p = monoconsts (class_qualifier_type p)
instance Has_Monotypes Class_Qualifier where
monotypes q = [class_qualifier_type q]
monotypes_map f q = q{class_qualifier_type = f (class_qualifier_type q)}
-- | Return the 'Unification' (when it exists)
-- of two given 'Class_Qualifier's,
-- using given 'Synotype_Substitution'.
class_qualifier_unification
:: Synotype_Substitution
-> Class_Qualifier
-> Class_Qualifier
-> Maybe Unification
class_qualifier_unification syns
(Class_Qualifier na1 ty1)
(Class_Qualifier na2 ty2)
| na1 == na2 =
case mgu_with_synotypes syns (constify ty1) ty2 of
Left _ -> Nothing
Right (_, uni) -> Just (unconstify <$> uni)
| otherwise = Nothing
-- ** Type 'Class_Env'
type Class_Env = Map Class_Name Class
type Class_Name = Text
type Class_Path = [Class_Name]
class_env_empty :: Class_Env
class_env_empty = Map.empty
class_env_default :: Class_Env
class_env_default = Map.fromList
[ ("Num",)
Class
{ class_supers = ["Eq", "Show"]
, class_instances =
[ Class_Instance (Class_Qualifier "Num" ty) []
| ty <- [type_Int, type_Float]
]
}
, ("Enum",)
Class
{ class_supers = []
, class_instances =
[ Class_Instance (Class_Qualifier "Enum" ty) []
| ty <- [type_Unit, type_Int, type_Float, type_Bool, type_Char]
]
}
, ("Eq",)
Class
{ class_supers = []
, class_instances =
Class_Instance
{ class_instance_head = Class_Qualifier "Eq" "Ordering"
, class_instance_context = []
} :
instances_Eq_Ord_Show "Eq"
}
, ("Ord",)
Class
{ class_supers = ["Eq"]
, class_instances = instances_Eq_Ord_Show "Ord"
}
, ("Show",)
Class
{ class_supers = []
, class_instances = instances_Eq_Ord_Show "Show"
}
]
where
instances_Eq_Ord_Show name =
Class_Instance -- NOTE: List instance
{ class_instance_head = Class_Qualifier name (type_List (Monotype_Var 0))
, class_instance_context = [Class_Qualifier name (Monotype_Var 0)]
} :
[ Class_Instance
{ class_instance_head = Class_Qualifier name ty
, class_instance_context = []
}
| ty <- [type_Bool, type_Char, type_Float, type_Int]
] <>
(inst_tuples <$> (0 : [2..10]))
where
inst_tuples i =
Class_Instance
{ class_instance_head =
Class_Qualifier name (type_Tuple [ Monotype_Var n | n <- [1..i] ])
, class_instance_context =
[ Class_Qualifier name (Monotype_Var n)
| n <- [1..i]
]
}
-- | Return given 'Class_Env'
-- with given 'Class_Name'
-- mapping to a 'Class'
-- with given 'Class_Instance'.
class_env_insert_instance
:: Class_Name
-> Class_Instance
-> Class_Env -> Class_Env
class_env_insert_instance name inst =
Map.insertWith
(\_new old -> old{class_instances = inst : class_instances old})
name Class
{ class_supers = []
, class_instances = [inst]
}
-- | Return whether given 'Class_Env'
-- has a 'Class' with given 'Class_Name'.
class_env_has :: Class_Env -> Class_Name -> Bool
class_env_has = flip Map.member
-- | Return the 'class_supers'
-- associated to the given 'Class_Name',
-- in given 'Class_Env'.
--
-- Example: @'class_env_supers' 'class_env_default' \"Ord\" '==' [\"Eq\"]@
class_env_supers :: Class_Env -> Class_Name -> [Class_Name]
class_env_supers env name = maybe [] class_supers $ Map.lookup name env
-- | Return the 'Class_Path's
-- between given 'Class_Name's
-- in given 'Class_Env'.
class_env_super_path
:: Class_Env
-> Class_Name -> Class_Name
-> [Class_Path]
class_env_super_path env from to
| from == to = [[to]]
| otherwise =
[ from : path
| super <- class_env_supers env from
, path <- class_env_super_path env super to
]
-- | Return the 'Class_Instance's
-- of given 'Class_Name' within given 'Class_Env'.
class_env_instances
:: Class_Env -> Class_Name -> [Class_Instance]
class_env_instances env name =
maybe [] class_instances $
Map.lookup name env
-- | Return all the 'Class_Qualifier's
-- of all the 'class_supers'
-- of the given 'Class_Qualifier',
-- within given 'Class_Env'.
class_env_supers_all
:: Class_Env
-> Class_Qualifier
-> [Class_Qualifier]
class_env_supers_all env p@(Class_Qualifier name ty) =
p : List.concat
[ class_env_supers_all env (Class_Qualifier supers ty)
| supers <- class_env_supers env name
]
-- | Return the 'class_instance_context'
-- 'substitute'd by the 'class_qualifier_unification'
-- of the first 'class_instance_head'
-- unifying with given 'Class_Qualifier',
-- within given 'Class_Env',
-- and using given 'Synotype_Substitution'.
class_env_instance_context
:: Synotype_Substitution
-> Class_Env
-> Class_Qualifier
-> Maybe [Class_Qualifier]
class_env_instance_context syns env qual@(Class_Qualifier qname _) =
Foldable.msum
[ context inst
| inst <- class_env_instances env qname
]
where
context :: Class_Instance -> Maybe [Class_Qualifier]
context Class_Instance{class_instance_head, class_instance_context} = do
uni <- class_qualifier_unification syns qual class_instance_head
Just (uni `substitute` class_instance_context)
-- ** Normalization
class_qualifier_normalize
:: Synotype_Substitution
-> Class_Env
-> Class_Qualifier
-> Maybe [Class_Qualifier]
class_qualifier_normalize syns env p
| is_class_qualifier_normalized p = Just [p]
| otherwise = do
ctx <- class_env_instance_context syns env p
class_context_normalize syns env ctx
is_class_qualifier_normalized :: Class_Qualifier -> Bool
is_class_qualifier_normalized (Class_Qualifier _ ty) = go ty
where
go (Monotype_Var _) = True
go (Monotype_Const _) = False
go (Monotype_App t _) = go t
class_context_normalize
:: Synotype_Substitution
-> Class_Env
-> [Class_Qualifier]
-> Maybe [Class_Qualifier]
class_context_normalize syns env ps =
List.concat <$>
mapM (class_qualifier_normalize syns env) ps
-- ** Entailment
class_entails
:: Synotype_Substitution
-> Class_Env
-> [Class_Qualifier]
-> Class_Qualifier
-> Bool
class_entails syns env quals qual =
class_entails_super_class env quals qual ||
case class_env_instance_context syns env qual of
Nothing -> False
Just insts -> Foldable.all (class_entails syns env quals) insts
-- | Return whether a given 'Class'
-- implies another as a 'class_supers'.
class_entails_super_class
:: Class_Env
-> [Class_Qualifier]
-> Class_Qualifier
-> Bool
class_entails_super_class env ps p =
Foldable.any
(p `List.elem`)
(class_env_supers_all env <$> ps)
class_entails_all
:: Synotype_Substitution
-> Class_Env
-> [Class_Qualifier]
-> [Class_Qualifier]
-> Bool
class_entails_all syns env ps =
Foldable.all (class_entails syns env ps)
-- ** Type 'Class_Reduction_Error'
newtype Class_Reduction_Error a
= Class_Reduction_Error a
deriving (Show)
-- | Reduce a 'Class' context by performing:
--
-- 1. Simplification using 'Class_Instance's.
--
-- Example: @Eq (a, b)@ is simplified to @Eq a@ and @Eq b@.
--
-- Example: @Eq Int@ is removed.
--
-- Example: @Num Bool@ (whose 'Class_Instance' does not exist)
-- gives a 'Class_Reduction_Error'.
--
-- 2. Removal of 'Class'es entailed by 'class_supers'.
--
-- Example: Because @Eq@ is the superclass of @Ord@, @Ord a@ 'class_entails' @Eq a@,
-- which therefore can safely be removed when @Ord a@ is present.
--
-- 3. Removal of duplicate 'Class'es.
class_context_reduction
:: Synotype_Substitution
-> Class_Env
-> [Class_Qualifier]
-> ( [Class_Qualifier]
, [Class_Reduction_Error Class_Qualifier]
)
class_context_reduction syns env =
first (go []) . List.foldr fold ([], [])
where
fold qual (quals, errs) =
case class_qualifier_normalize syns env qual of
Just qs -> (qs <> quals, errs)
Nothing -> (quals, Class_Reduction_Error qual : errs)
go rs [] = rs
go rs (q:qs)
| entailed = go rs qs
| otherwise = go (q:rs) qs
where entailed = class_entails_super_class env (rs <> qs) q
{-
context_reduction_associated
:: Synotype_Substitution
-> Class_Env
-> [(Class_Qualifier, a)]
-> ( [(Class_Qualifier, a)]
, [Class_Reduction_Error (Class_Qualifier, a)]
)
context_reduction_associated syns env =
first (go []) . List.foldr fold ([], [])
where
fold (qual, a) (reduced, es) =
case class_qualifier_normalize syns env qual of
Just qs -> ([(p, a) | p <- qs] <> reduced, es)
Nothing -> (reduced, Class_Reduction_Error (qual, a) : es)
go rs [] = rs
go rs (q:qs)
| entailed = go rs qs
| otherwise = go (q:rs) qs
where
entailed = class_entails_super_class env
(fst <$> (rs <> qs)) (fst q)
-}
-- * Type 'Class_Directive'
data Class_Directive info
= Class_Directive_Never Class_Qualifier info
| Class_Directive_Close Class_Name info
| Class_Directive_Disjoint [Class_Name] info
| Class_Directive_Default Class_Name [Monotype] info
deriving (Eq, Show)
instance Buildable info => Buildable (Class_Directive info) where
build _ = "Class_Directive"
-- FIXME: write it.