packages feed

free-foil-0.3.1: test/Control/Monad/Foil/NameMapSpec.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs     #-}

-- | Helpers for 'Foil.NameMap' and 'Foil.NameBinderList'.
module Control.Monad.Foil.NameMapSpec (spec) where

import           Data.Foldable      (toList)
import           Test.Hspec

import qualified Control.Monad.Foil as Foil

-- | The raw names bound by a list of binders, outermost first.
binderIds :: Foil.NameBinderList n l -> [Int]
binderIds Foil.NameBinderListEmpty = []
binderIds (Foil.NameBinderListCons binder binders) =
  Foil.nameId (Foil.nameOf binder) : binderIds binders

spec :: Spec
spec = do
  describe "withFreshNameBinderList" $ do
    it "binds each value to a fresh binder, in order" $
      Foil.withFreshNameBinderList "abc" Foil.emptyScope Foil.emptyNameMap $
        \_scope binders nameMap -> do
          binderIds binders `shouldBe` [0, 1, 2]   -- a fresh name is max scope + 1
          toList nameMap `shouldBe` "abc"

    it "binds nothing when there is nothing to bind" $
      Foil.withFreshNameBinderList ([] :: [Int]) Foil.emptyScope Foil.emptyNameMap $
        \_scope binders nameMap -> do
          binderIds binders `shouldBe` []
          toList nameMap `shouldBe` []

  describe "popNameBinder" $
    it "undoes addNameBinder" $
      Foil.withFresh Foil.emptyScope $ \binder ->
        let nameMap = Foil.addNameBinder binder 'x' Foil.emptyNameMap
         in toList (Foil.popNameBinder binder nameMap) `shouldBe` ""

  describe "snocNameBinderList" $
    it "adds a binder to the innermost position" $
      Foil.withFreshNameBinderList "ab" Foil.emptyScope Foil.emptyNameMap $
        \scope binders _nameMap ->
          Foil.withFresh scope $ \binder ->
            binderIds (Foil.snocNameBinderList binders binder) `shouldBe` [0, 1, 2]

  describe "concatNameBinderLists" $
    it "agrees with snocNameBinderList on a singleton" $
      Foil.withFreshNameBinderList "ab" Foil.emptyScope Foil.emptyNameMap $
        \scope binders _nameMap ->
          Foil.withFresh scope $ \binder ->
            let singleton = Foil.NameBinderListCons binder Foil.NameBinderListEmpty
             in binderIds (Foil.concatNameBinderLists binders singleton)
                  `shouldBe` binderIds (Foil.snocNameBinderList binders binder)