packages feed

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.