packages feed

lol-calculus-1.20160822: Language/LOL/Calculus/Type.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Language.LOL.Calculus.Type where

import Control.Arrow
import Control.Monad
import Data.Either (Either(..))
import Data.Eq (Eq(..))
import Data.Foldable (Foldable(..))
import Data.Function (($), (.), const)
import Data.Functor ((<$>))
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Maybe (Maybe(..))
import Data.Monoid (Monoid(..), (<>))
import Data.Ord (Ord(..))
import Data.Text.Buildable (Buildable(..))
import Data.Traversable (Traversable(..))
import Data.Typeable as Typeable
import Text.Show (Show(..))

import Language.LOL.Calculus.Abstraction
import Language.LOL.Calculus.Term
import Language.LOL.Calculus.Form

-- * Type 'Type'

-- | Construct the 'Type' of the given 'Term',
-- effectively checking for the /well-formedness/
-- and /well-typedness/ of a 'Term' (or 'Type').
--
-- Note that a 'Type' is always to be considered
-- according to a given 'Context':
-- 'type_of' applied to the same 'Term'
-- but on different 'Context's
-- may return a different 'Type' or 'Type_Error'.
type_of
 :: (Eq var, Ord var, Variable var, Typeable var)
 => Context var
 -> Term var
 -> Either Type_Error (Type var)
type_of ctx term =
	case term of
	 Type_Sort s -> (err +++ Type_Sort) $ sort_of_sort s
	 TeTy_Var v ->
		case form_normal
		 .   context_item_type
		 <$> context_lookup ctx v of
		 Just ty -> return ty
		 Nothing -> Left $ err $ Type_Error_Msg_Unbound_variable v
	 TeTy_Axiom ax ->
		return $ axiom_type_of ctx ax
	 TeTy_App f x -> do
		f_ty <- whnf ctx <$> type_of ctx f
		(f_in, f_out) <-
			case f_ty of
			 Type_Abst _ i o -> return (i, o)
			 _ -> Left $ err $ Type_Error_Msg_Not_a_function f f_ty
		x_ty <- type_of ctx x
		if equiv ctx x_ty f_in
			then return $ const x `unabstract` f_out
			else Left $ err $ Type_Error_Msg_Function_argument_mismatch f_in x_ty
	 Term_Abst (Suggest x) f_in f -> do
		_ <- type_of ctx f_in
		let new_ctx =
			if x == ""
			then context_push_nothing ctx
			else context_push_type ctx (Suggest x) f_in
		f_out <- type_of new_ctx (abstract_normalize f)
		let abst_ty = Type_Abst (Suggest x) f_in (abstract_generalize f_out)
		_ <- type_of ctx abst_ty
		return abst_ty
	 Type_Abst (Suggest x) f_in f -> do
		f_in_ty <- type_of ctx f_in
		f_in_so <- case whnf ctx f_in_ty of
		 Type_Sort s -> return s
		 f_in_ty_whnf -> Left $ err $ Type_Error_Msg_Invalid_input_type f_in_ty_whnf
		let new_ctx =
			if x == ""
			then context_push_nothing ctx
			else context_push_type ctx (Suggest x) f_in
		f_out_ty <- type_of new_ctx (abstract_normalize f)
		f_out_so <- case whnf new_ctx f_out_ty of
		 Type_Sort s -> return s
		 f_out_ty_whnf -> Left $ Type_Error ctx term $
			Type_Error_Msg_Invalid_output_type f_out_ty_whnf (x, f_out_ty_whnf)
		(err +++ Type_Sort) $
			sort_of_type_abst f_in_so f_out_so
	where
	err = Type_Error ctx term

-- | Check that the given 'Term' has the given 'Type'.
check
 :: (Eq var, Ord var, Variable var, Typeable var)
 => Context var
 -> Type var
 -> Term var
 -> Either Type_Error ()
check ctx expect_ty te =
	type_of ctx te >>= \actual_ty ->
		if equiv ctx expect_ty actual_ty
		then Right ()
		else Left Type_Error
			 { type_error_ctx  = ctx
			 , type_error_term = te
			 , type_error_msg  = Type_Error_Msg_Type_mismatch expect_ty actual_ty (normalize ctx expect_ty) (normalize ctx actual_ty)
			 }

-- | Check that a 'Term' is closed, i.e. has no /unbound variables/.
close :: Term Var_Name -> Either Type_Error (Term ())
close te =
	traverse go te
	where
		go var = Left $
			Type_Error context te $
			Type_Error_Msg_Unbound_variable var

-- | Return the /unbound variables/ of given 'Term'.
unbound_vars :: Ord var => Term var -> Map var ()
unbound_vars = foldr (`Map.insert` ()) mempty

-- | /Dependent product/ rules: @s ↝ t : u@, i.e.
--   "abstracting something of type @s@ out of something of type @t@ gives something of type @u@".
--
-- Given two 'Sort': @s@ and @t@, return a 'Sort': @u@,
-- when ('Type_Abst' @s@ @t@) is ruled legal
-- and has 'Type': 'Type_Sort' ('Sort' @u@).
--
-- The usual /PTS/ rules for /λω/
-- (or equivalently /Type Assignment Systems/ (TAS) rules for /System Fω/)
-- are used here:
--
-- * RULE: @⊦ * ↝ * : *@, aka. /simple types/:
--   "abstracting a term out of a term is valid and gives a term",
--   as in /PTS λ→/ or /TAS F1/.
-- * RULE: @⊦ □ ↝ * : *@, aka. /parametric polymorphism/:
--   "abstracting a type out of a term is valid and gives a term",
--   as in /PTS λ2/ or /TAS F2/ (aka. /System F/).
-- * RULE: @⊦ □ ↝ □ : □@, aka. /constructors of types/:
--   "abstracting a type out of a type is valid and gives a type",
--   as in /PTS λω/ or /TAS Fω/.
--
-- Note that the fourth usual rule is not ruled valid here:
--
-- * RULE: @⊦ * ↝ □ : □@, aka. /dependent types/:
--   "abstracting a term out of a type is valid and gives a type",
--   as in /PTS λPω/ or /TAS DFω/ (aka. /Calculus of constructions/).
--
-- However, to contain /impredicativity/ (see 'Axiom_MonoPoly')
-- the above /sort constants/ are split in two,
-- and the above rules adapted
-- to segregate between /monomorphic types/ (aka. /monotypes/)
-- and /polymorphic types/ (aka. /polytypes/):
--
-- * RULE: @⊦ *m ↝ *m : *m@, i.e. /simple types/, without /parametric polymorphism/.
-- * RULE: @⊦ *m ↝ *p : *p@, i.e. /simple types/, preserving /parametric polymorphism/ capture.
--
-- * RULE: @⊦ *p ↝ *m : *p@, i.e. /higher-rank polymorphism/, preserving /parametric polymorphism/ capture.
-- * RULE: @⊦ *p ↝ *p : *p@, i.e. /higher-rank polymorphism/, preserving /parametric polymorphism/ capture.
--
-- * RULE: @⊦ □m ↝ *m : *p@, i.e. /parametric polymorphism/, captured within @*p@ ('sort_star_poly').
-- * RULE: @⊦ □m ↝ *p : *p@, i.e. /parametric polymorphism/, preserving capture.
--
-- * RULE: @⊦ □m ↝ □m : □m@, i.e. /constructors of types/, without /parametric polymorphism/.
-- * RULE: @⊦ □m ↝ □p : □p@, i.e. /constructors of types/, preserving /parametric polymorphism/ capture.
--
-- Note that what is important here is
-- that there is no rule of the form: @⊦ □p ↝ _ : _@,
-- which forbids abstracting a /polymorphic type/ out of anything,
-- in particular the type @*p -> *m@ is forbidden,
-- though 'Axiom_MonoPoly'
-- is given to make it possible within it.
--
-- __Ressources:__
--
-- * /Henk: a typed intermediate language/,
--   Simon Peyton Jones, Erik Meijer, 20 May 1997,
--   https://research.microsoft.com/en-us/um/people/simonpj/papers/henk.ps.gz
sort_of_type_abst
 :: Sort
 -> Sort
 -> Either Type_Error_Msg Sort

-- Simple types
sort_of_type_abst
          (Type_Level_0, Type_Morphism_Mono)
          (Type_Level_0, m)
 = return (Type_Level_0, m)
 -- RULE: *m ↝ *m : *m
 -- RULE: *m ↝ *p : *p
 -- abstracting: a MONOMORPHIC term
 -- out of     : a MONOMORPHIC (resp. POLYMORPHIC) term
 -- forms      : a MONOMORPHIC (resp. POLYMORPHIC) term

-- Higher-rank
sort_of_type_abst
          (Type_Level_0, Type_Morphism_Poly)
          (Type_Level_0, _)
 = return (Type_Level_0, Type_Morphism_Poly)
 -- RULE: *p ↝ *m : *p
 -- RULE: *p ↝ *p : *p
 -- abstracting: a POLYMORPHIC term
 -- out of     : a             term
 -- forms      : a POLYMORPHIC term

-- Polymorphism
sort_of_type_abst
          (Type_Level_1, Type_Morphism_Mono)
          (Type_Level_0, _)
 = return (Type_Level_0, Type_Morphism_Poly)
 -- RULE: □m ↝ *m : *p
 -- RULE: □m ↝ *p : *p
 -- abstracting: a MONOMORPHIC type of a term
 -- out of     : a             term
 -- forms      : a POLYMORPHIC term

-- Type constructors
sort_of_type_abst
          (Type_Level_1, Type_Morphism_Mono)
          (Type_Level_1, m)
 = return (Type_Level_1, m)
 -- RULE: □m ↝ □m : □m
 -- RULE: □m ↝ □p : □p
 -- abstracting: a MONOMORPHIC type of a term
 -- out of     : a MONOMORPHIC (resp. POLYMORPHIC) type of a term
 -- forms      : a MONOMORPHIC (resp. POLYMORPHIC) type of a term
 --
 -- NOTE: □m ↝ □p : □p is useful for instance to build List_:
 -- let List_ : (A:*m) -> *p = \(A:*m) -> (R:*m) -> (A -> R -> R) -> R -> R
 -- let List  : (A:*m) -> *m = \(A:*m) -> Monotype (List_ A)

-- Dependent types

{-
sort_of_type_abst
          (Type_Level_0, Type_Morphism_Mono)
          (Type_Level_1, m)
 = return (Type_Level_1, m)
 -- RULE: *m ↝ □m : □m
 -- RULE: *m ↝ □p : □p
 -- abstracting: a MONOMORPHIC term
 -- out of     : a MONOMORPHIC (resp. POLYMORPHIC) type of a term
 -- forms      : a MONOMORPHIC (resp. POLYMORPHIC) type of a term

sort_of_type_abst
 (Type_Level_0, Type_Morphism_Poly)
 (Type_Level_1, _)
 = return (Type_Level_1, Type_Morphism_Poly)
 -- RULE: *p ↝ □m : □p
 -- RULE: *p ↝ □p : □p
 -- abstracting: a POLYMORPHIC term
 -- out of     : a             type of a term
 -- forms      : a POLYMORPHIC type of a term
-}

sort_of_type_abst
 s@(Type_Level_0, _)
 t@(Type_Level_1, _)
 = Left $ Type_Error_Msg_Illegal_type_abstraction s t
 -- RULE: * ↝ □ : Illegal
 -- abstracting: a term
 -- out of     : a type of a term
 -- is illegal

-- No impredicativity (only allowed through 'Axiom_MonoPoly')
sort_of_type_abst
 s@(Type_Level_1, Type_Morphism_Poly)
 t@(_, _)
 = Left $ Type_Error_Msg_Illegal_type_abstraction s t
 -- RULE: □p ↝ _ : Illegal
 -- abstracting: a POLYMORPHIC type of a term
 -- out of     : anything
 -- is illegal

-- ** Type 'Type_Error'
data Type_Error
 = forall var. (Ord var, Show var, Buildable var)
 =>  Type_Error
 {   type_error_ctx  :: Context var
 ,   type_error_term :: Term var
 ,   type_error_msg  :: Type_Error_Msg
 }
deriving instance Show Type_Error
instance Buildable Type_Error where
	build (Type_Error ctx te msg) =
		"Error: Type_Error"
		 <> "\n " <> build msg
		 <> "\n Term:" <> " " <> build te
		 <> (
			let vars =
				Map.keys $
				Map.intersection
				 (unbound_vars te)
				 (Map.fromList $ (, ()) <$> context_vars ctx) in
			case vars of
			 [] -> "\n"
			 _ -> "\n Context:\n" <> build ctx{context_vars=vars}
		 )

-- ** Type 'Type_Error_Msg'
data Type_Error_Msg
 =   Type_Error_Msg_No_sort_for_sort Sort
 |   Type_Error_Msg_Illegal_type_abstraction Sort Sort
 |   forall var. Variable var => Type_Error_Msg_Invalid_input_type  (Type var)
 |   forall var. Variable var => Type_Error_Msg_Invalid_output_type (Type var) (Var_Name, Type var)
 |   forall var. Variable var => Type_Error_Msg_Not_a_function (Term var) (Type var)
 |   forall var. Variable var => Type_Error_Msg_Function_argument_mismatch (Type var) (Type var)
 |   forall var. Variable var => Type_Error_Msg_Unbound_variable var
 |   forall var. Variable var => Type_Error_Msg_Unbound_axiom var
 |   forall var. Variable var => Type_Error_Msg_Type_mismatch (Type var) (Type var) (Type var) (Type var)
deriving instance Show Type_Error_Msg
instance Buildable Type_Error_Msg where
	build msg =
		case msg of
		 Type_Error_Msg_No_sort_for_sort x ->
			"No_sort_for_sort: "
			 <> build x
		 Type_Error_Msg_Illegal_type_abstraction x y ->
			"Illegal_type_abstraction: "
			 <> build x <> " -> " <> build y
		 Type_Error_Msg_Invalid_input_type ty ->
			"Invalid_input_type: "
			 <> build ty
		 Type_Error_Msg_Invalid_output_type f_out (x, f_in) ->
			"Invalid_output_type: "
			 <> build f_out <> "\n"
			 <> " Input binding: "
			 <> "(" <> build x <> " : " <> build f_in <> ")"
		 Type_Error_Msg_Not_a_function f f_ty ->
			"Not_a_function: "
			 <> build f
			 <> " : "
			 <> build f_ty
		 Type_Error_Msg_Function_argument_mismatch f_in x_ty ->
			"Function_argument_mismatch: \n"
			 <> " Function domain: " <> build f_in <> "\n"
			 <> " Argument type:   " <> build x_ty
		 Type_Error_Msg_Unbound_variable var ->
			"Unbound_variable: "
			 <> build var
		 Type_Error_Msg_Unbound_axiom var ->
			"Unbound_axiom: "
			 <> build var
		 Type_Error_Msg_Type_mismatch x y nx ny ->
			"Type_mismatch: \n"
			 <> " Expected type: " <> build x <> " == " <> build nx <> "\n"
			 <> " Actual   type: " <> build y <> " == " <> build ny

-- ** Type 'Sort'

-- *** Type 'Type_Level'

-- | /PTS/ axioms for 'Sort':
--
-- * AXIOM: @⊦ *m : □m@
-- * AXIOM: @⊦ *p : □p@
sort_of_sort :: Sort -> Either Type_Error_Msg Sort
sort_of_sort (Type_Level_0, Type_Morphism_Mono)
 = return (Type_Level_1, Type_Morphism_Mono)
 -- AXIOM: @*m : □m@
 -- The type of MONOMORPHIC types of terms,
 -- is of type: the type of types of MONOMORPHIC types of terms
sort_of_sort (Type_Level_0, Type_Morphism_Poly)
 = return (Type_Level_1, Type_Morphism_Poly)
 -- AXIOM: @*p : □p@
 -- The type of POLYMORPHIC types of terms,
 -- is of type: the type of types of POLYMORPHIC types of terms
sort_of_sort s = Left $ Type_Error_Msg_No_sort_for_sort s