hypertypes-0.2.2: test/LangBTest.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module LangBTest (test, withEnv) where
import qualified Control.Lens as Lens
import Control.Lens.Operators
import Control.Monad.RWS
import Control.Monad.ST (runST)
import Data.Functor.Identity (Identity (..))
import ExprUtils
import Hyper
import Hyper.Syntax.Nominal (NominalDecl (..), loadNominalDecl)
import Hyper.Syntax.Scheme
import Hyper.Unify
import LangB
import Test.Tasty
import TypeLang
import Prelude
test :: TestTree
test =
testGroup
"infer LangB"
[ testB letGen0 "Right Int"
, testB letGen1 "Right (∀t0(*). (Int -> Int -> t0) -> t0)"
, testB letGen2 "Right (∀t0(*). ∀r0(∌ [a]). ∀r1(∌ [a]). (a : (a : t0 :*: r0) :*: r1) -> t0)"
, testB genInf "Left (t0 occurs in itself, expands to: t0 -> t1)"
, testB shouldNotGen "Right (∀t0(*). t0 -> t0)"
, testB simpleRec "Right (a : Int :*: {})"
, testB extendLit "Left (Mismatch Int r0)"
, testB extendDup "Left (ConstraintsViolation (a : Int :*: {}) (∌ [a]))"
, testB extendGood "Right (b : Int :*: a : Int :*: {})"
, testB unifyRows "Right (((b : Int :*: a : Int :*: {}) -> Int -> Int) -> Int)"
, testB openRows "Right (∀r0(∌ [a, b, c]). (c : Int :*: r0) -> (b : Int :*: r0) -> ((c : Int :*: a : Int :*: b : Int :*: r0) -> Int -> Int) -> Int)"
, testB getAField "Right (∀t0(*). ∀r0(∌ [a]). (a : t0 :*: r0) -> t0)"
, testB vecApp "Right (∀t0(*). t0 -> t0 -> Vec[elem: t0])"
, testB usePhantom "Right (∀t0(*). PhantomInt[phantom: t0])"
, testB return5 "Right (∀r0(*). Mut[value: Int, effects: r0])"
, testB returnOk "Right LocalMut[value: Int]"
, testB nomSkolem0 "Left (SkolemEscape: r0)"
, testB nomSkolem1 "Left (SkolemEscape: r0)"
, testB nomSkolem2 "Left (SkolemEscape: r0)"
]
letGen0 :: HPlain LangB
letGen0 =
BLetP "id" (BLamP "x" "x") $
"id" `BAppP` "id" `BAppP` BLitP 5
letGen1 :: HPlain LangB
letGen1 =
BLetP "five" (BLitP 5) $
BLetP
"f"
(BLamP "x" ("x" `BAppP` "five" `BAppP` "five"))
"f"
letGen2 :: HPlain LangB
letGen2 =
BLetP "f" (BLamP "x" (BGetFieldP "x" "a")) $
BLamP "x" ("f" `BAppP` ("f" `BAppP` "x"))
genInf :: HPlain LangB
genInf =
BLetP
"f"
(BLamP "x" ("x" `BAppP` "x"))
"f"
shouldNotGen :: HPlain LangB
shouldNotGen =
BLamP
"x"
( BLetP
"y"
"x"
"y"
)
simpleRec :: HPlain LangB
simpleRec =
BRecExtendP
"a"
(BLitP 5)
BRecEmptyP
extendLit :: HPlain LangB
extendLit =
BRecExtendP "a" (BLitP 5) $
BLitP 7
extendDup :: HPlain LangB
extendDup =
BRecExtendP "a" (BLitP 7) $
BRecExtendP
"a"
(BLitP 5)
BRecEmptyP
extendGood :: HPlain LangB
extendGood =
BRecExtendP "b" (BLitP 7) $
BRecExtendP
"a"
(BLitP 5)
BRecEmptyP
getAField :: HPlain LangB
getAField = BLamP "x" (BGetFieldP "x" "a")
vecApp :: HPlain LangB
vecApp =
BLamP
"x"
( BLamP
"y"
( BToNomP "Vec" $
BRecExtendP "x" "x" $
BRecExtendP
"y"
"y"
BRecEmptyP
)
)
usePhantom :: HPlain LangB
usePhantom = BToNomP "PhantomInt" (BLitP 5)
unifyRows :: HPlain LangB
unifyRows =
BLamP "f" ("f" `BAppP` r0 `BAppP` ("f" `BAppP` r1 `BAppP` BLitP 12))
where
r0 =
BRecExtendP "a" (BLitP 5) $
BRecExtendP
"b"
(BLitP 7)
BRecEmptyP
r1 =
BRecExtendP "b" (BLitP 5) $
BRecExtendP
"a"
(BLitP 7)
BRecEmptyP
openRows :: HPlain LangB
openRows =
BLamP "x" $
BLamP "y" $
BLamP "f" $
"f" `BAppP` r0 `BAppP` ("f" `BAppP` r1 `BAppP` BLitP 12)
where
r0 =
BRecExtendP "a" (BLitP 5) $
BRecExtendP "b" (BLitP 7) $
BVarP "x"
r1 =
BRecExtendP "c" (BLitP 5) $
BRecExtendP "a" (BLitP 7) $
BVarP "y"
return5 :: HPlain LangB
return5 = "return" `BAppP` BLitP 5
returnOk :: HPlain LangB
returnOk = BToNomP "LocalMut" return5
nomSkolem0 :: HPlain LangB
nomSkolem0 = BLamP "x" (BToNomP "LocalMut" "x")
nomSkolem1 :: HPlain LangB
nomSkolem1 = nomSkolem0 `BAppP` return5
nomSkolem2 :: HPlain LangB
nomSkolem2 = BToNomP "LocalMut" ("newMutRef" `BAppP` BLitP 3)
vecNominalDecl :: Pure # NominalDecl Typ
vecNominalDecl =
_Pure
# NominalDecl
{ _nParams =
Types
{ _tRow = mempty
, _tTyp = mempty & Lens.at "elem" ?~ mempty
}
, _nScheme =
Scheme
{ _sForAlls = Types mempty mempty
, _sTyp =
( REmptyP
& RExtendP "x" (TVarP "elem")
& RExtendP "y" (TVarP "elem")
& TRecP
)
^. hPlain
}
}
phantomIntNominalDecl :: Pure # NominalDecl Typ
phantomIntNominalDecl =
_Pure
# NominalDecl
{ _nParams =
Types
{ _tRow = mempty
, _tTyp = mempty & Lens.at "phantom" ?~ mempty
}
, _nScheme =
Scheme
{ _sForAlls = Types mempty mempty
, _sTyp = _Pure # TInt
}
}
mutTypeH :: Name -> HPlain Row -> HPlain Typ -> HPlain Typ
mutTypeH t eff res =
TNomP
t
Types
{ _tRow = mempty & Lens.at "effects" ?~ eff ^. hPlain & QVarInstances
, _tTyp = mempty & Lens.at "value" ?~ res ^. hPlain & QVarInstances
}
mutType :: HPlain Row -> HPlain Typ -> HPlain Typ
mutType = mutTypeH "Mut"
-- A nominal type with foralls:
-- "newtype LocalMut a = forall s. Mut s a"
localMutNominalDecl :: Pure # NominalDecl Typ
localMutNominalDecl =
_Pure
# NominalDecl
{ _nParams =
Types
{ _tRow = mempty
, _tTyp = mempty & Lens.at "value" ?~ mempty
}
, _nScheme =
forAll (Const ()) (Identity "effects") (\_ (Identity eff) -> mutType eff (TVarP "value")) ^. _Pure
}
returnScheme :: Pure # Scheme Types Typ
returnScheme =
forAll
(Identity "value")
(Identity "effects")
(\(Identity val) (Identity eff) -> TFunP val (mutType eff val))
unitToUnitScheme :: Pure # Scheme Types Typ
unitToUnitScheme =
forAll Proxy Proxy (\Proxy Proxy -> TFunP (TRecP REmptyP) (TRecP REmptyP))
newMutRefScheme :: Pure # Scheme Types Typ
newMutRefScheme =
forAll
(Identity "value")
(Identity "effects")
(\(Identity val) (Identity eff) -> TFunP val (mutType eff (mutTypeH "MutRef" eff val)))
withEnv ::
( UnifyGen m Row
, MonadReader env m
, HasScheme Types m Typ
) =>
Lens.LensLike' Identity env (InferScope (UVarOf m)) ->
m a ->
m a
withEnv l act =
do
vec <- loadNominalDecl vecNominalDecl
phantom <- loadNominalDecl phantomIntNominalDecl
localMut <- loadNominalDecl localMutNominalDecl
let addNoms x =
x
& Lens.at "Vec" ?~ vec
& Lens.at "PhantomInt" ?~ phantom
& Lens.at "LocalMut" ?~ localMut
ret <- loadScheme returnScheme
newMutRef <- loadScheme newMutRefScheme
unitToUnit <- loadScheme unitToUnitScheme
let addEnv x =
x
& nominals %~ addNoms
& varSchemes . _ScopeTypes . Lens.at "return" ?~ MkHFlip ret
& varSchemes . _ScopeTypes . Lens.at "newMutRef" ?~ MkHFlip newMutRef
& varSchemes . _ScopeTypes . Lens.at "unitToUnit" ?~ MkHFlip unitToUnit
local (l %~ addEnv) act
testB :: HPlain LangB -> String -> TestTree
testB p expect =
testCommon expr expect pureRes stRes
where
expr = p ^. hPlain
pureRes = execPureInferB (withEnv id (inferExpr expr))
stRes = runST (execSTInferB (withEnv Lens._1 (inferExpr expr)))