sbv-14.1: Documentation/SBV/Examples/TP/NatStream.hs
-----------------------------------------------------------------------------
-- |
-- Module : Documentation.SBV.Examples.TP.NatStream
-- Copyright : (c) Levent Erkok
-- License : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Demonstrating productive (corecursive) functions. A productive function
-- is one where every recursive call is guarded by a data constructor, so
-- the function always makes progress by producing output. Unlike terminating
-- functions, productive functions need not have a base case and may produce
-- infinite output.
-----------------------------------------------------------------------------
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeAbstractions #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wall -Werror #-}
module Documentation.SBV.Examples.TP.NatStream where
import Prelude hiding (head, length, (!!))
import Data.SBV
import Data.SBV.List
import Data.SBV.TP
#ifdef DOCTEST
-- $setup
-- >>> import Data.SBV.TP
#endif
-- * Definitions
-- | The infinite stream of integers starting from @n@: @[n, n+1, n+2, ...]@.
-- There is no base case; every recursive call is guarded by the list
-- constructor @.:@, making this a productive (corecursive) definition.
nats :: SInteger -> SList Integer
nats = smtProductiveFunction "nats"
$ \n -> n .: nats (n + 1)
-- * Correctness
-- | Prove that @nats n@ always starts with @n@.
--
-- NB. As of Mar 2026, z3 can't handle this but cvc5 can.
--
-- >>> runTP natsHead
-- Lemma: natsHead Q.E.D.
-- Functions proven productive: nats
-- [Proven] natsHead :: Ɐn ∷ Integer → Bool
natsHead :: TP (Proof (Forall "n" Integer -> SBool))
natsHead = lemmaWith cvc5 "natsHead"
(\(Forall @"n" n) -> head (nats n) .== n)
[]
-- | Prove by induction that @nats n@ has at least @m@ elements, for any @m@.
-- This captures the idea that @nats@ produces an unboundedly long list.
--
-- NB. As of Mar 2026, z3 can't handle this but cvc5 can.
--
-- >>> runTP natsLen
-- Inductive lemma: natsLen
-- Step: Base Q.E.D.
-- Step: 1 Q.E.D.
-- Step: 2 Q.E.D.
-- Result: Q.E.D.
-- Functions proven productive: nats
-- [Proven] natsLen :: Ɐm ∷ Integer → Ɐn ∷ Integer → Bool
natsLen :: TP (Proof (Forall "m" Integer -> Forall "n" Integer -> SBool))
natsLen =
inductWith cvc5 "natsLen"
(\(Forall @"m" m) (Forall @"n" n) -> length (nats n) .>= m) $
\ih m n -> []
|- length (nats n) .>= m + 1
=: length (n .: nats (n + 1)) .>= m + 1
?? ih `at` Inst @"n" (n + 1)
=: sTrue
=: qed
-- | Prove by induction that the @k@-th element of @nats n@ is @n + k@.
--
-- NB. As of Mar 2026, z3 can't handle this but cvc5 can.
--
-- >>> runTP natsElem
-- Lemma: natsLen Q.E.D.
-- Lemma: elemOne Q.E.D.
-- Inductive lemma: natsElem
-- Step: Base Q.E.D.
-- Step: 1 Q.E.D.
-- Step: 2 Q.E.D.
-- Step: 3 Q.E.D.
-- Step: 4 Q.E.D.
-- Result: Q.E.D.
-- Functions proven productive: nats
-- [Proven] natsElem :: Ɐk ∷ Integer → Ɐn ∷ Integer → Bool
natsElem :: TP (Proof (Forall "k" Integer -> Forall "n" Integer -> SBool))
natsElem = do
nLen <- recall natsLen
elemOne <- lemma "elemOne"
(\(Forall @"x" (x :: SInteger)) (Forall @"y" y) (Forall @"k" k) ->
k .> 0 .&& k .<= length y .=> (x .: y) !! k .== y !! (k - 1))
[]
inductWith cvc5 "natsElem"
(\(Forall @"k" k) (Forall @"n" n) -> k .>= 0 .=> nats n !! k .== n + k) $
\ih k n -> [k .>= 0]
|- nats n !! (k + 1)
=: (n .: nats (n + 1)) !! (k + 1)
?? elemOne
?? nLen
=: nats (n + 1) !! k
?? ih `at` Inst @"n" (n + 1)
=: (n + 1) + k
=: n + (k + 1)
=: qed