lol-typing-1.20160822: Language/LOL/Typing/Solver/Test.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Solver.Test where
import Control.Arrow (second)
import Control.Monad (Monad(..))
import Data.Bool
import Data.Either (Either(..))
import Data.Function (($), (.))
import Data.Functor ((<$>))
-- import Data.Functor.Identity (Identity(..))
import Data.Int (Int)
import qualified Data.List as List
import qualified Data.Map.Strict as Map
import Data.Maybe ({-Maybe(..),-} fromMaybe)
import Data.Monoid ((<>))
import Data.Sequence (Seq)
import Data.String (IsString(..))
-- import Data.Text (Text)
import qualified Data.Text as Text
import Data.Text.Buildable (Buildable(..))
-- import qualified Data.Text.IO as Text
import qualified Data.Text.Lazy.Builder as Build
import qualified Data.Text.Lazy.IO as TL
-- import Data.Tuple (fst)
import Prelude (Num(..), error)
import System.IO (IO)
-- import Test.HUnit hiding (test)
import Test.Tasty
import Test.Tasty.HUnit
import Text.Show (Show(..))
import Language.LOL.Typing.Type
import Language.LOL.Typing.Expr
import Language.LOL.Typing.Solver
import Language.LOL.Typing.Collect.Constraint
-- import Language.LOL.Typing.Collect.Grammar
import qualified Language.LOL.Typing.Collect.Grammar as Collect
import Language.LOL.Typing.Lib.Data.Default (Default(..))
import qualified Language.LOL.Typing.Lib.Control.Monad.Classes.Instance as MC
import qualified Language.LOL.Typing.Lib.Data.Text.Buildable as Build
-- Convenient instances
instance IsString Polytype where
fromString = polytype . Monotype_Const . fromString
instance Num Monotype where
(+) = Monotype_App
(*) = Monotype_App
abs x = x
signum _ = 0
negate x = x
fromInteger = Monotype_Var . fromInteger
type Test_Solver
= Solver_Greedy_Finite Collect_Infos IO
{-
polytype_of_expr
:: Expr
-> Either (Collect_Error (Info Test_Solver)
(Error Test_Solver))
Text
polytype_of_expr ex = do
-- Text.putStrLn (Text.pack $ show ex)
let Syn_Expr
{ constraints_Syn_Expr = cs
, freshvar_Syn_Expr = freshvar
, monoty_Syn_Expr = monotype
, errors_Syn_Expr = errors
} = wrap_Expr (sem_Expr ex) Inh_Expr
{ polytys_Inh_Expr = -- Map.fromList
[ ("eq", polytyref
(Quantification [1] [(1, "a")] $
[Class_Qualifier "Eq" 1]
.=>. (1.->.1.->."Bool")
:: Polytype))
]
, freshvar_Inh_Expr = 0
}
case errors of
(_:_) -> Left $ Collect_Error_Grammar errors
[] ->
let cs' = {-hussel-} cs in
let cfg = solver_config
{ solver_config_freshvar = freshvar
, solver_config_check = True
} in
let (result :: Solver_Result (Info Test_Solver) (Error Test_Solver)) =
runIdentity $
solve (\_ -> return ()) $
solver_greedy_finite cfg cs' in
case solver_result_errors result of
[] -> Right $ Build.text $
(def::Quantification_Build_Options,) $
for_all $ solver_result_monotypes result `substitute` monotype
errs -> Left $ Collect_Error_Solver errs
-- | Like 'polytype_of_expr', but more verbose;
-- mainly for debugging.
debug_expr :: Expr -> IO Text
debug_expr ex = do
Text.putStrLn (Text.pack $ Build.string ex)
let Syn_Expr
{ constraints_Syn_Expr = cs
, freshvar_Syn_Expr = freshvar
, monoty_Syn_Expr = monotype
, errors_Syn_Expr = errors
} = wrap_Expr (sem_Expr ex) Inh_Expr
{ polytys_Inh_Expr = mempty
, freshvar_Inh_Expr = 0
}
case errors of
(_:_) -> return $ Build.text $
show (Collect_Error_Grammar errors::Collect_Error Collect_Info (Error Test_Solver))
[] -> do
let cs' = {-hussel-} cs
let cfg = solver_config
{ solver_config_freshvar = freshvar
, solver_config_check = True
}
(result :: Solver_Result (Info (Solver_Greedy_Finite Collect_Infos IO))
(Error (Solver_Greedy_Finite Collect_Infos IO))) <-
solve write_log $
solver_greedy_finite cfg cs'
-- (\_ -> return ())
return $
case solver_result_errors result of
[] -> Build.text $
(def::Quantification_Build_Options,) $
for_all $ solver_result_monotypes result `substitute` monotype
errs -> Build.text $ show $ Collect_Error_Solver errs
-}
write_log :: Solver_Log -> IO ()
write_log l =
case l of
Solver_Log_Constraint x ->
TL.putStrLn $ Build.toLazyText $ "- " <> build x
Solver_Log_Class x ->
TL.putStrLn $ Build.toLazyText $ "- " <> build x
Solver_Log_Polytype x ->
TL.putStrLn $ Build.toLazyText $ "- " <> build x
Solver_Log_States states ->
TL.putStrLn $ Build.toLazyText $
Build.unlines $
[ hline ] <>
List.zipWith
(\i (MC.Instance s) ->
build i <> ". " <> build (state_name s) <> "\n" <>
Build.indent " " (Build.unlines $
(build <$> state_options s) <>
[build $ state_show s]))
[1::Int ..]
states <>
[ hline ]
where
hline :: Build.Builder = build $ Text.replicate 80 "-"
polytys_env :: [(Name, Polytype)]
polytys_env =
[ ("equal", polytype
(Quantification [1] [(1, "a")] $
[Class_Qualifier "Eq" 1]
.=>. (1.->.1.->."Bool")
:: Polytype))
, ("compare", polytype
(Quantification [1] [(1, "a")] $
[Class_Qualifier "Ord" 1]
.=>. (1.->.1.->."Ordering")
:: Polytype))
, ("True", polytype
(Quantification [] [] $
[] .=>. "Bool"
:: Polytype))
, ("one", polytype
(Quantification [] [] $
[] .=>. "Int"
:: Polytype))
, ("pair", polytype
(Quantification [1,2] [] $
[] .=>. (1.->.2.->.type_Tuple [1,2])
:: Polytype))
]
class Collect_Constraints a where
collect_constraints
:: a -> Either (Collect_Error (Info Test_Solver) (Error Test_Solver))
(Freshvar, Seq Collect_Constraint)
instance Collect_Constraints Expr where
collect_constraints expr =
let Collect.Syn_Expr
{ Collect.freshvar_Syn_Expr = freshvar
, Collect.constraints_Syn_Expr = constrs
, Collect.errors_Syn_Expr = errors
} = Collect.wrap_Expr (Collect.sem_Expr expr)
Collect.Inh_Expr
{ Collect.polytys_Inh_Expr = (polytyref `second`) <$> polytys_env
, Collect.freshvar_Inh_Expr = 0
} in
case errors of
errs@(_:_) -> Left $ Collect_Error_Grammar errs
[] -> Right (freshvar, constrs)
instance Collect_Constraints Decl where
collect_constraints decl =
let Collect.Syn_Decl
{ Collect.freshvar_Syn_Decl = freshvar
, Collect.constraints_Syn_Decl = constrs
, Collect.errors_Syn_Decl = errors
} = Collect.wrap_Decl (Collect.sem_Decl decl)
Collect.Inh_Decl
{ Collect.polytys_Inh_Decl = (polytyref `second`) <$> polytys_env
, Collect.freshvar_Inh_Decl = 0
} in
case errors of
errs@(_:_) -> Left $ Collect_Error_Grammar errs
[] -> Right (freshvar, constrs)
infer :: Collect_Constraints a
=> Bool -> a
-> IO (Either (Collect_Error (Info Test_Solver) (Error Test_Solver))
(Solver_Result (Info Test_Solver) (Error Test_Solver)))
infer logging x =
case collect_constraints x of
Left err -> return $ Left err
Right (solver_config_freshvar, constrs) -> do
let cfg = solver_config
{ solver_config_freshvar
, solver_config_check = True
}
let wlog = if logging then write_log else (\_ -> return ())
res <- solve wlog $ solver_greedy_finite cfg constrs
return $
case solver_result_errors res of
[] -> Right res
errs -> Left $ Collect_Error_Solver errs
class Collect_Constraints a => Infer_Polytype a where
infer_polytype
:: Bool -> a
-> IO (Either (Collect_Error (Info Test_Solver) (Error Test_Solver))
Polytype)
instance Infer_Polytype Expr where
infer_polytype logging ex = do
res <- infer logging ex
case res of
Left err -> return $ Left err
Right Solver_Result
{ solver_result_monotypes = monosub
, solver_result_qualifiers = quals
} -> return $ Right $
for_all $ (quals .=>.) $
(monosub `substitute`) $
Monotype_Var 0
instance Infer_Polytype Decl where
infer_polytype logging ex = do
res <- infer logging ex
case res of
Left err -> return $ Left err
Right Solver_Result
{ solver_result_monotypes = monosub
, solver_result_polytypes = polysub
} -> return $ Right $
(monosub `substitute`) $
fromMaybe (error $ "Oops, Polytype missing" <> show polysub) $
Map.lookup 0 polysub
tests :: TestTree
tests = testGroup "Solver"
[ testGroup "Expr" $
let test logging input expected = do
p <- infer_polytype logging input
let got = (Build.text . (def::Quantification_Build_Options,)) <$> p
got @?= expected in
let (==>) = test False in
-- let (==>>) = test True in
[ testGroup "SKI" $
-- DOC: https://en.wikipedia.org/wiki/SKI_combinator_calculus
-- DOC: https://en.wikipedia.org/wiki/B,_C,_K,_W_system
let i = "x".-> "x" in
let k = "x".-> "y".-> "x" in
let s = "x".-> "y".-> "z".-> ("x"!"z") ! ("y"!"z") in
let c = "f".-> "x".-> "y".-> "f"!"y"!"x" in
let b = "f".-> "g".-> "x".-> "f"!("g"!"x") in
let w = "x".-> "y".-> "x"!"y"!"y" in
let t = k in -- True
let f = s ! k in -- False
let no = f ! t in -- not
[ testCase "I" $ i
==> Right "forall a. a -> a"
, testCase "K" $ k
==> Right "forall a b. a -> b -> a"
, testCase "S" $ s
==> Right "forall a b c. (a -> b -> c) -> (a -> b) -> a -> c"
, testCase "B" $ b
==> Right "forall a b c. (a -> b) -> (c -> a) -> c -> b"
, testCase "B = S (K S) K" $
(s ! (k!s) ! k)
==> Right "forall a b c. (a -> b) -> (c -> a) -> c -> b"
, testCase "C" $ c
==> Right "forall a b c. (a -> b -> c) -> b -> a -> c"
, testCase "C = S (B B S) (K K)" $
(s ! (b!b!s) ! (k!k))
==> Right "forall a b c. (a -> b -> c) -> b -> a -> c"
, testCase "W" $ w
==> Right "forall a b. (a -> a -> b) -> a -> b"
, testCase "W = S S (S K)" $
(s ! s ! (s!k))
==> Right "forall a b. (a -> a -> b) -> a -> b"
, testCase "W /= S I I" $
(s!i!i)
==> Left (Collect_Error_Solver
[( Collect_Infos
[ Collect_Info_Solver (Solver_Info_Monotype $ Info_Monotype_Unification 1 (18 .->. 0))
, Collect_Info_Grammar (Collect_Info_Grammar_Expr "App") ]
, Solver_Error_Monotype $
Error_Monotype_Unification $
Unification_Error_Infinite_type 12
)])
, testCase "W W /= W W" $
(w ! w)
==> Left (Collect_Error_Solver
[( Collect_Infos
[ Collect_Info_Solver (Solver_Info_Monotype $ Info_Monotype_Unification 1 (10 .->. 0))
, Collect_Info_Grammar (Collect_Info_Grammar_Expr "App") ]
, Solver_Error_Monotype $
Error_Monotype_Unification $
Unification_Error_Infinite_type 13
)])
, testCase "S = B (B W) (B B C)" $
(b ! (b!w) ! (b!b!c))
==> Right "forall a b c. (a -> b -> c) -> (a -> b) -> a -> c"
, testCase "I = S K K" $
(s!k!k)
==> Right "forall a. a -> a"
, testCase "I = W K" $
(w ! k)
==> Right "forall a. a -> a"
, testCase "not" $ no
==> Right "forall a. a -> a"
]
, testGroup "Abst" $
let o = "f".-> "g".-> "x".-> "f"!("g"!"x") in
[ testCase "(x:Int) -> x" $
(("x", "Int"::Monotype)..-> "x")
==> Right "Int -> Int"
, testCase "(x:Int) -> (x:Bool)" $
(("x", "Int"::Monotype)..-> ("x".:("Bool"::Monotype)))
==> Left (Collect_Error_Solver
[ ( Collect_Infos
[ Collect_Info_Solver (Solver_Info_Monotype (Info_Monotype_Unification 2 "Bool"))
, Collect_Info_Solver (Solver_Info_Polytype (Info_Polytype_Rigidified [1] "Bool"))
, Collect_Info_Grammar (Collect_Info_Grammar_Expr "Annot Bool") ]
, Solver_Error_Monotype (Error_Monotype_Unification (Unification_Error_Constant_clash "Int" "Bool")))
])
, testCase "(x:Int -> Int) -> x" $
(("x", "Int".->."Int")..-> "x")
==> Right "(Int -> Int) -> Int -> Int"
, testCase "x -> y" $
("x".-> "y")
==> Left (Collect_Error_Grammar [Collect_Error_Grammar_Variable_not_in_scope "y"])
, testCase "compose" $ o
==> Right "forall a b c. (a -> b) -> (c -> a) -> c -> b"
, testCase "twice" $
("f".-> "x".-> "f"!("f"!"x"))
==> Right "forall a. (a -> a) -> a -> a"
, testCase "twice = \\f -> f . f" $
("f".-> "c" .= o $ "c"!"f"!"f")
==> Right "forall a. (a -> a) -> a -> a"
, testCase "(x y) z" $
("x".-> "y".-> "z".-> "x"!"y"!"z")
==> Right "forall a b c. (a -> b -> c) -> a -> b -> c"
, testCase "x (y z)" $
("x".-> "y".-> "z".-> "x"!("y"!"z"))
==> Right "forall a b c. (a -> b) -> (c -> a) -> c -> b"
, testCase "x u v t" $
("x".-> "y".-> "z".-> "t".->
"y" ! ("u".-> ("t" ! (("z"!"u") ! ("v".-> "x"!"u"!"v"!"t")))))
==> Right "forall a b c d e f. (a -> b -> (c -> d) -> e) -> ((a -> d) -> f) -> (a -> (b -> e) -> c) -> (c -> d) -> f"
, testCase "x -> x x" $
("x".-> "x"!"x")
==> Left (Collect_Error_Solver
[( Collect_Infos
[ Collect_Info_Solver (Solver_Info_Monotype $ Info_Monotype_Unification 3 (4 .->. 2))
, Collect_Info_Grammar (Collect_Info_Grammar_Expr "App") ]
, Solver_Error_Monotype $
Error_Monotype_Unification $
Unification_Error_Infinite_type 1
)])
, testCase "y -> (x -> x) y y" $
("y".-> ("x".-> "x")!"y"!"y")
==> Left (Collect_Error_Solver
[( Collect_Infos
[ Collect_Info_Solver (Solver_Info_Monotype $ Info_Monotype_Unification 3 (8 .->. 2))
, Collect_Info_Grammar (Collect_Info_Grammar_Expr "App") ]
, Solver_Error_Monotype $
Error_Monotype_Unification $
Unification_Error_Infinite_type 1
)])
, testCase "x -> (a b c -> a c) (x -> x) (f -> f x x)" $
("x".-> ("a".-> "b".-> "c".-> "a"!"c") ! ("x".-> "x") ! ("f".-> "f"!"x"!"x"))
==> Right "forall a b. a -> b -> b"
]
, testGroup "Let"
[ testCase "top_example_4_7" $
("x".->
"f".= ("g".= "x" $ "a".-> "g") $
"h".= "f" $ "h"!"x")
==> Right "forall a. a -> a"
, testCase "(i:Int) -> let (f:Int) = i in f" $
(("i", "Int"::Monotype)..->
(("f", "Int"::Monotype)..= "i") "f")
==> Right "Int -> Int"
, testCase "i -> let (f:forall a. a -> a) = i in f" $
("i".-> (("f", for_all ([] .=>. (1 .->. 1)))..= "i") "f")
==> Left (Collect_Error_Solver
[( Collect_Infos [ Collect_Info_Grammar (Collect_Info_Grammar_Expr "Let(sig) forall a. a -> a") ]
, Solver_Error_Polytype (Error_Polytype_Rigid_escaping [6])
)])
, testCase "(i:Int) -> let (f:Bool) = i in f" $
(("i", "Int"::Monotype)..->
(("f", "Bool"::Monotype)..= "i") "f")
==> Left (Collect_Error_Solver
[( Collect_Infos
[ Collect_Info_Solver (Solver_Info_Monotype (Info_Monotype_Unification 4 (Monotype_Const "Bool")))
, Collect_Info_Solver (Solver_Info_Polytype (Info_Polytype_Rigidified [1] "Bool"))
, Collect_Info_Grammar (Collect_Info_Grammar_Expr "Let(sig) Bool") ]
, Solver_Error_Monotype $
Error_Monotype_Unification $
Unification_Error_Constant_clash "Int" "Bool"
)])
, testCase "a -> b -> a" $
("x".-> "f".= ("y".->"x") $ "f")
==> Right "forall a b. a -> b -> a"
]
, testGroup "Annot" $
let id = "x".-> "x" in
[ testCase "id : a -> a" $
(id .: for_all ([] .=>. (1 .->. 1)))
==> Right "forall a. a -> a"
, testCase "id : Int -> Int" $
(id .: for_all ([] .=>. ("Int" .->. "Int")))
==> Right "Int -> Int"
, testCase "id /: a -> Int" $
(id .: for_all ([] .=>. (1 .->. "Int")))
==> Left (Collect_Error_Solver
[( Collect_Infos [ Collect_Info_Grammar (Collect_Info_Grammar_Expr "Annot forall a. a -> Int") ]
, Solver_Error_Polytype $
Error_Polytype_Rigid_type_mismatch
[(3, Monotype_Const "Int")] )
])
, testCase "id /: a -> b" $
(id .: for_all ([] .=>. (1 .->. 2)))
==> Left (Collect_Error_Solver
[( Collect_Infos [ Collect_Info_Grammar (Collect_Info_Grammar_Expr "Annot forall a b. a -> b") ]
, Solver_Error_Polytype $
Error_Polytype_Rigid_injectivity_lost $
Map.fromList [(3, [3, 4])] )
])
]
{-
, testGroup "Class" $
let (==>) input (expected::Text) = do
got <- ((Build.text . (def::Quantification_Build_Options,)) <$>)
<$> polytype_of_expr input
-- Text.putStrLn ("exp: " <> Build.text term)
got @?= Right expected in
[ testCase "Eq a => a -> Bool" $
("x".-> "=="!"x"!"x")
-- ("f".= ("x".-> "=="!"x"!"x") "f")
==> "forall a. Eq a => a -> Bool"
]
, testGroup "Class" $
let test logging input (expected::Text) = do
p <- polytype_of_decl logging input
let got = (Build.text . (def::Quantification_Build_Options,)) <$> p
got @?= Right expected in
let (==>) = test False in
let (==>>) = test True in
[ testCase "Eq a => a -> a -> Bool" $
Decl_Let Nothing "f" "=="
==> "forall a. Eq a => a -> a -> Bool"
, testCase "Eq a => a -> Bool" $
Decl_Let Nothing "f"
("x".-> "=="!"x"!"x")
==> "forall a. Eq a => a -> Bool"
, testCase "Eq a => a -> Bool" $
Decl_Let Nothing "f"
("x".-> "=="!("=="!"x"!"x")!("=="!"x"!"x"))
==> "forall a. Eq a => a -> Bool"
, testCase "Ord a => a -> Bool" $
Decl_Let Nothing "f" ("x".->
"=="
!("=="!"x"!"x")
!("=="
!("compare"!"x"!"x")
!("compare"!"x"!"x")))
==> "forall a. Ord a => a -> Bool"
, testCase "a -> b -> a" $
Decl_Let Nothing "f"
("x".-> "g".= ("y".->"x") $ "g")
==>> "forall a b. a -> b -> a"
]
-} ]
]