------------------------------------------------------------------------ -- 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))