------------------------------------------------------------------------
-- The Agda standard library
--
-- Simple examples of programs using IO
------------------------------------------------------------------------

{-# OPTIONS --guardedness #-}

module README.IO where

open import Level
open import Data.Nat.Base
open import Data.Nat.Show using (show)
open import Data.String.Base using (String; _++_; lines)
open import Data.Unit.Polymorphic
open import Function.Base using (_$_)
open import IO

------------------------------------------------------------------------
-- Basic programs
------------------------------------------------------------------------

------------------------------------------------------------------------
-- Hello World!

-- Minimal example of an IO program.
-- * The entrypoint of the executable is given type `Main`
-- * It is implemented using `run`, a function that converts a description
--   of an IO-computation into a computation that actually invokes the magic
--   primitives that will perform the side effects.

helloWorld : Main
helloWorld = run (putStrLn "Hello World!")

------------------------------------------------------------------------
-- Hello {{name}}!

-- We can of course write little auxiliary functions that may be used in
-- larger IO programs. Here we are going to first write a function displaying
-- "Hello {{name}}!" when {{name}} is passed as an argument.

-- `IO` primitives whose sole purpose is generating side effects (e.g.
-- printing a string on the screen) are typically given a level polymorphic
-- type which means we may need to add explicit level annotations.
-- Here we state that the `IO` computation will be at level zero (`0ℓ`).

sayHello : String → IO {0ℓ} ⊤
sayHello name = putStrLn ("Hello " ++ name ++ "!")

-- Functions can be sequenced using monadic combinators or `do` notations.
-- The two following definitions are equivalent. They start by asking the
-- user what their name is, listen for an answer and respond by saying hello
-- using the `sayHello` auxiliary function we just defined.

helloName : Main
helloName = run (putStrLn "What is your name?" >> getLine >>= sayHello)

doHelloName : Main
doHelloName = run do
  putStrLn "What is your name?"
  name ← getLine
  sayHello name

------------------------------------------------------------------------
-- (Co)Recursive programs
------------------------------------------------------------------------

------------------------------------------------------------------------
-- NO GUARDEDNESS

-- If you do not need to rely on guardedness for the function to be seen as
-- terminating (for instance because it is structural in an inductive argument)
-- then you can use `do` notations to write fairly readable programs.

-- Countdown to explosion
countdown : ℕ → IO {0ℓ} _
countdown zero      = putStrLn "BOOM!"
countdown m@(suc n) = do
  let str = show m
  putStrLn str
  countdown n

-- cat the content of a finite file
cat : String → IO _
cat fp = do
  content ← readFiniteFile fp
  let ls = lines content
  List.mapM′ putStrLn ls

------------------------------------------------------------------------
-- TOP-LEVEL LOOP

-- If you simply want to repeat the same action over and over again, you
-- can use `forever` e.g. the following defines a REPL that echos whatever
-- the user types

echo : IO ⊤
echo = do
  hSetBuffering stdout noBuffering
  forever $ do
    putStr "echo< "
    str ← getLine
    putStrLn ("echo> " ++ str)


------------------------------------------------------------------------
-- GUARDEDNESS

-- If you are performing coinduction on a potentially infinite piece of codata
-- then you need to rely on guardedness. That is to say that the coinductive
-- call needs to be obviously under a coinductive constructor and guarded by a
-- sharp (♯_).
-- In this case you cannot use the convenient combinators that make `do`-notations
-- and have to revert back to the underlying coinductive constructors.

open import Codata.Musical.Notation
open import Codata.Musical.Colist using (Colist; []; _∷_)
open import Data.Bool
open import Data.Unit.Polymorphic.Base

-- Whether a colist is finite is semi decidable: just let the user wait until
-- you reach the end!
isFinite : ∀ {a} {A : Set a} → Colist A → IO Bool
isFinite []       = pure true
isFinite (x ∷ xs) = seq (♯ pure tt) (♯ isFinite (♭ xs))
