packages feed

lambda-calculator-3.1.0.0: test/Language/Lambda/SystemF/Examples/NatSpec.hs

module Language.Lambda.SystemF.Examples.NatSpec where

import RIO
import Test.Hspec

import Language.Lambda.SystemF (evalText)
import Language.Lambda.SystemF.HspecUtils

spec :: Spec
spec = describe "Nat" $ do
  -- Nat is the definition of natural numbers. More precisely, Nat
  -- is the set of nonnegative integers.  We represent nats using
  -- Church Encodings:
  --
  -- 0: \f:(T->T) x:T. x
  -- 1: \f:(T->T) x:T. f x
  -- 2: \f:(T->T) x:T. f (f x)
  -- ...and so on

  describe "successor" $ do
    -- successor is a function that adds 1
    -- succ(0) = 1
    -- succ(1) = 2
    -- ... and so forth
    --
    -- successor is defined by
    -- succ = \n:((T->T)->T->T) f:(T->T) x:T. f (n f x)
    it "succ 0 = 1" $ do
      "(\\n:((T->T)->T->T) f:(T->T) x:T. f (n f x)) (\\f:(T->T) x:T. x)"
        `shouldEvalTo` "\\f:(T->T) x:T. f x"

    it "succ 1 = 2" $
      "(\\n:((T->T)->T->T) f:(T->T) x:T. f (n f x)) (\\f:(T->T) x:T. f x)"
        `shouldEvalTo` "\\f:(T->T) x:T. f (f x)"

  describe "add" $ do
    -- add(m, n) = m + n
    --
    -- It is defined by applying successor m times on n:
    -- add = \m:((T->T)->T->T) n:((T->T)->T->T) f:(T->T) x:T. m f (n f x)
    it "add 0 2 = 2" $
      "(\\m:((T->T)->T->T) n:((T->T)->T->T) f:(T->T) x:T. m f (n f x)) (\\f:(T->T) x:T. x) (\\f:(T->T) x:T. f (f x))"
        `shouldEvalTo` "\\f:(T->T) x:T. f (f x)"

    it "add 3 2 = 5" $
      "(\\m:((T->T)->T->T) n:((T->T)->T->T) f:(T->T) x:T. m f (n f x)) (\\f:(T->T) x:T. f (f (f x))) (\\f:(T->T) x:T. f (f x))"
        `shouldEvalTo` "\\f:(T->T) x:T. f (f (f (f (f x))))"

    it "add 0 n = n" $
      "(\\m:((T->T)->T->T) n:((T->T)->T->T) f:(T->T) x:T. m f (n f x)) (\\f:(T->T) x:T. x) n:((T->T)->T->T)"
        `shouldEvalTo` "\\f:(T->T) x:T. n:((T->T)->T->T) f x"

  describe "multiply" $ do
    -- multiply(m, n) = m * n
    --
    -- multiply is defined by applying add m times
    -- multiply = \m n f x. m (n f x) x)
    --
    -- Using eta conversion, we can omit the parameter x
    -- multiply = \m n f. m (n f)
    it "multiply 0 2 = 0" $
      "(\\m:((T->T)->T->T) n:((T->T)->T->T) f:(T->T). m (n f)) (\\f:(T->T) x:T. x) (\\f:(T->T) x:T. f (f x))"
        `shouldEvalTo` "\\f:(T->T) x:T. x"

    it "multiply 2 3 = 6" $
      "(\\m:((T->T)->T->T) n:((T->T)->T->T) f:(T->T). m (n f)) (\\f:(T->T) x:T. f (f x)) (\\f:(T->T) x:T. f (f (f x)))"
        `shouldEvalTo` "\\f:(T->T) x:T. f (f (f (f (f (f x)))))"

    it "multiply 0 n = 0" $
      "(\\m:((T->T)->T->T) n:((T->T)->T->T) f:(T->T). m (n f)) (\\f:(T->T) x:T. x) n:((T->T)->T->T)"
        `shouldEvalTo` "\\f:(T->T) x:T. x"

    it "multiply 1 n = n" $
      "(\\m:((T->T)->T->T) n:((T->T)->T->T) f:(T->T). m (n f)) (\\f:(T->T) x:T. f x) n:((T->T)->T->T)"
        `shouldEvalTo` "\\f:(T->T) x:T. n:((T->T)->T->T) f x"