packages feed

free-foil-0.6.1: test/Control/Monad/Free/Foil/UnresolvedNamesSpec.hs

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE GADTs             #-}
{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE RankNTypes        #-}
{-# LANGUAGE TemplateHaskell   #-}

-- | 'unresolvedNames' and 'unresolvedNamesWith' on a small raw λ-calculus,
-- against what 'tryConvertToASTWith' reports.
module Control.Monad.Free.Foil.UnresolvedNamesSpec (spec) where

import           Data.Bifunctor.TH
import           Data.Map                (Map)
import qualified Data.Map                as Map
import           Test.Hspec

import qualified Control.Monad.Foil      as Foil
import           Control.Monad.Free.Foil
import           Data.ZipMatchK.TH       (deriveZipMatchK)

data LamSig scope term
  = App term term
  | Lam scope
  deriving (Functor, Foldable, Traversable)

deriveBifunctor ''LamSig
deriveBifoldable ''LamSig
deriveBitraversable ''LamSig
deriveZipMatchK ''LamSig

type Term = AST Foil.NameBinder LamSig

data Raw = RVar String | RApp Raw Raw | RLam String Raw

toSig :: Raw -> Either String (LamSig (String, Raw) Raw)
toSig = \case
  RVar x   -> Left x
  RApp f a -> Right (App f a)
  RLam x b -> Right (Lam (x, b))

fromRawPattern
  :: Foil.Distinct n
  => Foil.Scope n
  -> Map String (Foil.Name n)
  -> String
  -> (forall l. Foil.DExt n l => Foil.NameBinder n l -> Map String (Foil.Name l) -> r)
  -> r
fromRawPattern scope names x cont = Foil.withFresh scope $ \binder ->
  cont binder (Map.insert x (Foil.nameOf binder) (fmap Foil.sink names))

-- | The constant @c@, which stands for λy. y.
constants :: Map String (Term Foil.VoidS)
constants = Map.singleton "c" $ Foil.withFresh Foil.emptyScope $ \y ->
  Node (Lam (ScopedAST y (Var (Foil.nameOf y))))

-- | c (λx. x d)
raw :: Raw
raw = RApp (RVar "c") (RLam "x" (RApp (RVar "x") (RVar "d")))

spec :: Spec
spec = do
  describe "unresolvedNames" $
    it "reports a constant as unresolved" $
      unresolvedNames toSig fromRawPattern id Foil.emptyScope Map.empty raw
        `shouldBe` [UnresolvedName "c" [], UnresolvedName "d" ["x"]]

  describe "unresolvedNamesWith" $ do
    it "resolves the constants" $
      unresolvedNamesWith toSig fromRawPattern id Foil.emptyScope Map.empty constants raw
        `shouldBe` [UnresolvedName "d" ["x", "c"]]
    it "reports first what tryConvertToASTWith reports" $
      either Just (const Nothing)
        (tryConvertToASTWith toSig fromRawPattern id Foil.emptyScope Map.empty constants raw)
        `shouldBe` Just (UnresolvedName "d" ["x", "c"])