packages feed

hydra-0.8.0: src/test/haskell/Hydra/Inference/AltInferenceSpec.hs

module Hydra.Inference.AltInferenceSpec where

import Hydra.Kernel
import Hydra.Inference.AltInference
import qualified Hydra.Tier1 as Tier1

import qualified Test.Hspec as H
import qualified Test.QuickCheck as QC
import qualified Data.List as L
import qualified Data.Map as M
import qualified Test.Hspec as H
import qualified Test.HUnit.Lang as HL
import qualified Hydra.Dsl.Types as Types


-- @wisnesky's original Algorithm W test cases, modified so as to normalize type variables
-- Polymorphic recursion is excluded; see checkPolymorphicRecursion
checkAlgorithmW :: H.SpecWith ()
checkAlgorithmW = H.describe "Check System F syntax" $ do
  --Untyped input:
  --	(\x. x)
  --System F type:
  -- 	(v0 -> v0)
  testCase "0"
    (lambda "x" $ var "x")
    (Types.poly ["t0"] $ Types.function (Types.var "t0") (Types.var "t0"))

  --Untyped input:
  --	letrecs foo = (\x. x)
  --		in 42
  --System F type:
  -- 	Nat
  testCase "1"
    (int32 32 `with` [
      "foo">: lambda "x" $ var "x"])
    (Types.mono Types.int32)

  --Untyped input:
  --	let f = (\x. x) in (f 0)
  --System F type:
  -- 	Nat
  testCase "2"
    ((var "f" @@ int32 0) `with` [
      "f">: lambda "x" $ var "x"])
    (Types.mono Types.int32)

  --Untyped input:
  --	let f = ((\x. x) 0) in f
  --System F type:
  -- 	Nat
  testCase "3"
    (var "f" `with` [
      "f">: (lambda "x" $ var "x") @@ int32 0])
    (Types.mono Types.int32)

  testCase "3.5"
    (lambda "x" $ list [var "x"])
    (Types.poly ["t0"] $ Types.function (Types.var "t0") (Types.list (Types.var "t0")))

  --Untyped input:
  --	let sng = (\x. (cons x nil)) in sng
  --System F type:
  -- 	(v5 -> (List v5))
  testCase "4"
    (var "sng" `with` [
      "sng">: lambda "x" $ list [var "x"]])
    (Types.poly ["t0"] $ Types.function (Types.var "t0") (Types.list (Types.var "t0")))

  --Untyped input:
  --	let sng = (\x. (cons x nil)) in (pair (sng 0) (sng alice))
  --System F type:
  -- 	((List Nat) * (List String))
  testCase "5"
    (pair (var "sng" @@ int32 0) (var "sng" @@ string "alice") `with` [
      "sng">: lambda "x" $ list [var "x"]])
    (Types.mono $ Types.pair (Types.list Types.int32) (Types.list Types.string))

  --Untyped input:
  --	letrecs + = (\x. (\y. (S (+ (P x) y))))
  --		in (+ (S (S 0)) (S 0))
  --System F type:
  -- 	Nat
  testCase "6"
    ((var "+" @@ (primSucc @@ (primSucc @@ int32 0)) @@ (primSucc @@ int32 0)) `with` [
      "+">: lambda "x" $ lambda "y" (primSucc @@ (var "+" @@ (primPred @@ var "x") @@ var "y"))])
    (Types.mono Types.int32)

checkApplication :: H.SpecWith ()
checkApplication = H.describe "Check application terms" $ do

  testCase "1"
    ((lambda "x" $ var "x") @@ (int32 42))
    (Types.mono Types.int32)

  testCase "2"
    (lambda "y" ((lambda "x" $ list [var "x"]) @@ (var "y")))
    (Types.poly ["t0"] $ Types.function (Types.var "t0") (Types.list $ Types.var "t0"))

checkLambdas :: H.SpecWith ()
checkLambdas = H.describe "Check lambda expressions" $ do

  testCase "1"
     (lambda "x" $ int32 42)
     (Types.poly ["t0"] (Types.function (Types.var "t0") Types.int32))

  testCase "2"
    (lambda "x" $ var "x")
    (Types.poly ["t0"] $ Types.function (Types.var "t0") (Types.var "t0"))

  testCase "3"
    (lambda "x" $ lambda "y" $ var "x")
    (Types.poly ["t0", "t1"] $ Types.function (Types.var "t0") (Types.function (Types.var "t1") (Types.var "t0")))

checkLists :: H.SpecWith ()
checkLists = H.describe "Check lists" $ do

  testCase "0"
    (list [])
    (Types.poly ["t0"] (Types.list $ Types.var "t0"))

  testCase "1"
    (list [int32 42])
    (Types.mono (Types.list Types.int32))

  testCase "2"
    (list [int32 42, int32 43])
    (Types.mono (Types.list Types.int32))

  testCase "3"
    (list [list []])
    (Types.poly ["t0"] (Types.list $ Types.list $ Types.var "t0"))

  testCase "4"
    (list [list [], list []])
    (Types.poly ["t0"] (Types.list $ Types.list $ Types.var "t0"))

  testCase "5"
    (list [list [], list [int32 42]])
    (Types.mono (Types.list $ Types.list Types.int32))

checkLambdasAndLists :: H.SpecWith ()
checkLambdasAndLists = H.describe "Check lambdas with lists" $ do

  testCase "0"
    (lambda "x" $ list [var "x"])
    (Types.poly ["t0"] $ Types.function (Types.var "t0") (Types.list (Types.var "t0")))

  testCase "1"
    (lambda "x" $ list [var "x", var "x"])
    (Types.poly ["t0"] $ Types.function (Types.var "t0") (Types.list (Types.var "t0")))

  testCase "2"
    (lambda "x" $ list [var "x", int32 42])
    (Types.mono $ Types.function Types.int32 (Types.list Types.int32))

  testCase "3"
    (lambda "x" $ lambda "y" $ list [var "x", int32 42, var "y"])
    (Types.mono $ Types.function Types.int32 $ Types.function Types.int32 $ Types.list Types.int32)

-- Additional test cases from @wisnesky which involve polymorphic recursion,
-- and so are not expected to be supported.
checkPolymorphicRecursion :: H.SpecWith ()
checkPolymorphicRecursion = H.describe "Check selected polymorphic recursion cases" $ do
  --Untyped input:
  --	letrecs f = (\x. (\y. (f 0 x)))
  --		in f
  --System F type:
  -- 	(Nat -> (Nat -> v5))
  testCase "7"
    (var "f" `with` [
      "f">: lambda "x" $ lambda "y" (var "f" @@ int32 0 @@ var "x")])
    (Types.poly ["t0"] $ Types.function Types.int32 (Types.function Types.int32 (Types.var "t0")))

  --Untyped input:
  --	letrecs f = (\x. (\y. (g 0 x)))
  --		g = (\u. (\v. (f v 0)))
  --		in (pair f g)
  --System F type:
  -- 	((v12 -> (Nat -> v13)) * (Nat -> (v15 -> v16)))
  testCase "9"
    ((pair (var "f") (var "g")) `with` [
      "f">: lambda "x" $ lambda "y" (var "g" @@ int32 0 @@ var "x"),
      "g">: lambda "u" $ lambda "v" (var "f" @@ var "v" @@ int32 0)])
    (Types.poly ["t0", "t1", "t2", "t3"] $ Types.pair
      (Types.function (Types.var "t0") (Types.function Types.int32 (Types.var "t1")))
      (Types.function Types.int32 (Types.function (Types.var "t2") (Types.var "t3"))))

  --Untyped input:
  --	letrecs f = (\x. (\y. (g 0 0)))
  --		g = (\u. (\v. (f v 0)))
  --		in (pair f g)
  --System F type:
  -- 	((Nat -> (Nat -> v12)) * (Nat -> (Nat -> v14)))
  testCase "10"
    ((pair (var "f") (var "g")) `with` [
      "f">: lambda "x" $ lambda "y" (var "g" @@ int32 0 @@ int32 0),
      "g">: lambda "u" $ lambda "v" (var "f" @@ var "v" @@ int32 0)])
    (Types.poly ["t0", "t1"] $ Types.pair
      (Types.function Types.int32 (Types.function Types.int32 (Types.var "t0")))
      (Types.function Types.int32 (Types.function Types.int32 (Types.var "t1"))))

  --Untyped input:
  --	letrecs f = (\x. (\y. (g 0 x)))
  --		g = (\u. (\v. (f 0 0)))
  --		in (pair f g)
  --System F type:
  -- 	((Nat -> (Nat -> v12)) * (Nat -> (Nat -> v14)))
  testCase "11"
    ((pair (var "f") (var "g")) `with` [
      "f">: lambda "x" $ lambda "y" (var "g" @@ int32 0 @@ var "x"),
      "g">: lambda "u" $ lambda "v" (var "f" @@ int32 0 @@ int32 0)])
    (Types.poly ["t0", "t1"] $ Types.pair
      (Types.function Types.int32 (Types.function Types.int32 (Types.var "t0")))
      (Types.function Types.int32 (Types.function Types.int32 (Types.var "t1"))))

expectType :: Term -> TypeScheme -> H.Expectation
expectType term expected = shouldSucceedWith (sInferType term) expected

testCase name term typ = H.it ("test #" ++ name) $ expectType term typ

shouldSucceedWith :: (Eq a, Show a) => Flow SInferenceContext a -> a -> H.Expectation
shouldSucceedWith f x = case my of
    Nothing -> HL.assertFailure "Unknown error" -- TODO: get error message from trace
    Just y -> y `H.shouldBe` x
  where
    FlowState my _ trace = unFlow f sInitialContext Tier1.emptyTrace

spec :: H.Spec
spec = do
  checkAlgorithmW
  checkApplication
  checkLambdas
  checkLists
  checkLambdasAndLists
--  checkPolymorphicRecursion