Skip to content

Commit eb338fd

Browse files
authored
[ refactor ] IO module (#1364)
1 parent edba5a5 commit eb338fd

File tree

5 files changed

+161
-37
lines changed

5 files changed

+161
-37
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ Non-backwards compatible changes
5959
to `Data.List.Relation.Binary.BagAndSetEquality` as their current location
6060
were causing cyclic import dependencies.
6161

62+
* Clean up of `IO` to make it more friendly:
63+
+ Renamed `_>>=_` and `_>>_` to `bind` and `seq` respectively to free up the names
64+
for `do`-notation friendly combinators.
65+
+ Introduced `Colist` and `List` modules for the various `sequence` and `mapM` functions.
66+
This breaks code that relied on the `Colist`-specific function being exported as part of `IO`.
67+
68+
6269
Deprecated modules
6370
------------------
6471

@@ -389,3 +396,8 @@ Other minor additions
389396
function : Func S S
390397
id-⟶ : A ⟶ A
391398
```
399+
400+
* Added new function to `Data.String.Base`:
401+
```agda
402+
lines : String → List String
403+
```

README/Foreign/Haskell.agda

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,11 @@ open import Relation.Nullary.Negation
8888

8989
-- example program using uncons, catMaybes, and testChar
9090

91-
main = run $
92-
readFiniteFile "README/Foreign/Haskell.agda" {- read this file -} >>= λ f
93-
let chars = toList f in
94-
let cleanup = catMaybes ∘ List.map (λ c if testChar c then just c else nothing) in
95-
let cleaned = dropWhile ('\n' ≟_) $ cleanup chars in
91+
main = run $ do
92+
content readFiniteFile "README/Foreign/Haskell.agda"
93+
let chars = toList content
94+
let cleanup = catMaybes ∘ List.map (λ c if testChar c then just c else nothing)
95+
let cleaned = dropWhile ('\n' ≟_) $ cleanup chars
9696
case uncons cleaned of λ where
9797
nothing putStrLn "I cannot believe this file is filed with dashes only!"
9898
(just (c , cs)) putStrLn $ unlines

README/IO.agda

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
------------------------------------------------------------------------
2+
-- The Agda standard library
3+
--
4+
-- Simple examples of programs using IO
5+
------------------------------------------------------------------------
6+
7+
{-# OPTIONS --guardedness #-}
8+
9+
module README.IO where
10+
11+
open import Data.Nat.Base
12+
open import Data.Nat.Show using (show)
13+
open import Data.String.Base using (String; lines)
14+
open import Function.Base using (_$_)
15+
open import IO
16+
open import System.Environment
17+
18+
------------------------------------------------------------------------
19+
-- NO GUARDEDNESS
20+
21+
-- If you do not need to rely on guardedness for the function to be seen as
22+
-- terminating (for instance because it is structural in an inductive argument)
23+
-- then you can use `do` notations to write fairly readable programs.
24+
25+
-- Countdown to explosion
26+
countdown : IO _
27+
countdown zero = putStrLn "BOOM!"
28+
countdown m@(suc n) = do
29+
let str = show m
30+
putStrLn str
31+
countdown n
32+
33+
-- cat the content of a finite file
34+
cat : String IO _
35+
cat fp = do
36+
content readFiniteFile fp
37+
let ls = lines content
38+
List.mapM′ putStrLn ls
39+
40+
open import Codata.Musical.Notation
41+
open import Codata.Musical.Colist
42+
open import Data.Bool
43+
open import Data.Unit.Polymorphic.Base
44+
45+
------------------------------------------------------------------------
46+
-- GUARDEDNESS
47+
48+
-- If you are performing coinduction on a potentially infinite piece of codata
49+
-- then you need to rely on guardedness. That is to say that the coinductive
50+
-- call needs to be obviously under a coinductive constructor and guarded by a
51+
-- sharp (♯_).
52+
-- In this case you cannot use the convenient combinators that make `do`-notations
53+
-- and have to revert back to the underlying coinductive constructors.
54+
55+
56+
-- Whether a colist is finite is semi decidable: just let the user wait until
57+
-- you reach the end!
58+
isFinite : {a} {A : Set a} Colist A IO Bool
59+
isFinite [] = return true
60+
isFinite (x ∷ xs) = seq (♯ return tt) (♯ isFinite (♭ xs))

src/Data/String/Base.agda

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,6 @@ intersperse sep = concat ∘′ (List.intersperse sep)
8989

9090
-- String-specific functions
9191

92-
unlines : List String String
93-
unlines = intersperse "\n"
94-
9592
wordsBy : {p} {P : Pred Char p} Decidable P String List String
9693
wordsBy P? = List.map fromList ∘ List.wordsBy P? ∘ toList
9794

@@ -101,6 +98,12 @@ words = wordsBy (T? ∘ Char.isSpace)
10198
unwords : List String String
10299
unwords = intersperse " "
103100

101+
lines : String List String
102+
lines = wordsBy ('\n' Char.≟_)
103+
104+
unlines : List String String
105+
unlines = intersperse "\n"
106+
104107
parens : String String
105108
parens s = "(" ++ s ++ ")"
106109

src/IO.agda

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@
99
module IO where
1010

1111
open import Codata.Musical.Notation
12-
open import Codata.Musical.Colist
1312
open import Codata.Musical.Costring
1413
open import Data.Unit.Polymorphic
1514
open import Data.String
1615
open import Function
1716
import IO.Primitive as Prim
1817
open import Level
1918

19+
private
20+
variable
21+
a b : Level
22+
2023
------------------------------------------------------------------------
2124
-- The IO monad
2225

@@ -27,50 +30,96 @@ open import Level
2730
-- introduced to avoid this problem. Possible non-termination is
2831
-- isolated to the run function below.
2932

30-
infixl 1 _>>=_ _>>_
31-
32-
data IO {a} (A : Set a) : Set (suc a) where
33+
data IO (A : Set a) : Set (suc a) where
3334
lift : (m : Prim.IO A) IO A
3435
return : (x : A) IO A
35-
_>>=_ : {B : Set a} (m : ∞ (IO B)) (f : (x : B) ∞ (IO A)) IO A
36-
_>>_ : {B : Set a} (m₁ : ∞ (IO B)) (m₂ : ∞ (IO A)) IO A
36+
bind : {B : Set a} (m : ∞ (IO B)) (f : (x : B) ∞ (IO A)) IO A
37+
seq : {B : Set a} (m₁ : ∞ (IO B)) (m₂ : ∞ (IO A)) IO A
38+
39+
pure : {A : Set a} A IO A
40+
pure = return
41+
42+
module _ {A B : Set a} where
43+
44+
infixl 1 _<$>_ _<*>_ _>>=_ _>>_
45+
46+
_<*>_ : IO (A B) IO A IO B
47+
mf <*> mx = bind (♯ mf) λ f ♯ (bind (♯ mx) λ x ♯ pure (f x))
48+
49+
_<$>_ : (A B) IO A IO B
50+
f <$> m = pure f <*> m
51+
52+
_>>=_ : IO A (A IO B) IO B
53+
m >>= f = bind (♯ m) λ x ♯ f x
54+
55+
_>>_ : IO A IO B IO B
56+
m₁ >> m₂ = seq (♯ m₁) (♯ m₂)
3757

3858
{-# NON_TERMINATING #-}
3959

40-
run : {a} {A : Set a} IO A Prim.IO A
41-
run (lift m) = m
42-
run (return x) = Prim.return x
43-
run (m >>= f) = Prim._>>=_ (run (♭ m )) λ x run (♭ (f x))
44-
run (m₁ >> m₂) = Prim._>>=_ (run (♭ m₁)) λ _ run (♭ m₂)
60+
run : {A : Set a} IO A Prim.IO A
61+
run (lift m) = m
62+
run (return x) = Prim.return x
63+
run (bind m f) = Prim._>>=_ (run (♭ m )) λ x run (♭ (f x))
64+
run (seq m₁ m₂) = Prim._>>=_ (run (♭ m₁)) λ _ run (♭ m₂)
4565

4666
------------------------------------------------------------------------
4767
-- Utilities
4868

49-
sequence : {a} {A : Set a} Colist (IO A) IO (Colist A)
50-
sequence [] = return []
51-
sequence (c ∷ cs) = ♯ c >>= λ x
52-
♯ (♯ sequence (♭ cs) >>= λ xs
53-
♯ return (x ∷ ♯ xs))
69+
module Colist where
70+
71+
open import Codata.Musical.Colist
72+
73+
module _ {A : Set a} where
74+
75+
sequence : Colist (IO A) IO (Colist A)
76+
sequence [] = return []
77+
sequence (c ∷ cs) = bind (♯ c) λ x
78+
bind (♯ sequence (♭ cs)) λ xs
79+
return (x ∷ ♯ xs)
80+
81+
-- The reason for not defining sequence′ in terms of sequence is
82+
-- efficiency (the unused results could cause unnecessary memory use).
83+
84+
sequence′ : Colist (IO A) IO ⊤
85+
sequence′ [] = return _
86+
sequence′ (c ∷ cs) = seq (♯ c) (♯ sequence′ (♭ cs))
87+
88+
module _ {A : Set a} {B : Set b} where
89+
90+
mapM : (A IO B) Colist A IO (Colist B)
91+
mapM f = sequence ∘ map f
92+
93+
mapM′ : (A IO B) Colist A IO ⊤
94+
mapM′ f = sequence′ ∘ map f
95+
96+
module List where
97+
98+
open import Data.List.Base
99+
100+
module _ {A : Set a} where
101+
102+
sequence : List (IO A) IO (List A)
103+
sequence [] = ⦇ [] ⦈
104+
sequence (c ∷ cs) = ⦇ c ∷ sequence cs ⦈
54105

55-
-- The reason for not defining sequence′ in terms of sequence is
56-
-- efficiency (the unused results could cause unnecessary memory use).
106+
-- The reason for not defining sequence′ in terms of sequence is
107+
-- efficiency (the unused results could cause unnecessary memory use).
57108

58-
sequence′ : {a} {A : Set a} Colist (IO A) IO ⊤
59-
sequence′ [] = return _
60-
sequence′ (c ∷ cs) = ♯ c >>
61-
♯ (♯ sequence′ (♭ cs) >>
62-
♯ return _)
109+
sequence′ : List (IO A) IO ⊤
110+
sequence′ [] = return _
111+
sequence′ (c ∷ cs) = c >> sequence′ cs
63112

64-
module _ {a b} {A : Set a} {B : Set b} where
113+
module _ {A : Set a} {B : Set b} where
65114

66-
mapM : (A IO B) Colist A IO (Colist B)
67-
mapM f = sequence ∘ map f
115+
mapM : (A IO B) List A IO (List B)
116+
mapM f = sequence ∘ map f
68117

69-
mapM′ : (A IO B) Colist A IO ⊤
70-
mapM′ f = sequence′ ∘ map f
118+
mapM′ : (A IO B) List A IO ⊤
119+
mapM′ f = sequence′ ∘ map f
71120

72121
ignore : {a} {A : Set a} IO A IO ⊤
73-
ignore io = io >> return _
122+
ignore io = io >> return _
74123

75124
------------------------------------------------------------------------
76125
-- Simple lazy IO

0 commit comments

Comments
 (0)