Skip to content

Instantly share code, notes, and snippets.

@mietek
Forked from gelisam/Main.agda
Created July 30, 2017 01:59
Show Gist options
  • Select an option

  • Save mietek/241b7bb5f83f535a9aa5488785af266c to your computer and use it in GitHub Desktop.

Select an option

Save mietek/241b7bb5f83f535a9aa5488785af266c to your computer and use it in GitHub Desktop.

Revisions

  1. @gelisam gelisam revised this gist Dec 7, 2016. 1 changed file with 10 additions and 49 deletions.
    59 changes: 10 additions & 49 deletions Main.agda
    Original file line number Diff line number Diff line change
    @@ -19,7 +19,6 @@ import IO.Primitive as Prim
    record ⊤₁ : Set₁ where
    constructor tt₁


    -- The IO module from stdlib is a wrapper around Prim.IO based on musical notation, plus a
    -- NON_TERMINATING wrapper named run. Let's define our own wrapper using copatterns instead.
    --
    @@ -43,36 +42,12 @@ run ioB with isDone ioB | step ioB
    ... | just b | _ = Prim.return b
    ... | nothing | (A , ioA , ccB) = Prim._>>=_ ioA (run ∘ ccB)


    -- To define return, _>>=_, and any other function returning an IO value, we define what each
    -- To define return, and any other function returning an IO value, we define what each
    -- destructor should return on the resulting IO value.

    return : {A} A IO A
    isDone (return a) = just a
    step (return a) = tt₁

    _>>=_ : {B C} IO B (B IO C) IO C
    _>>=_ {B} {C} ioB f with isDone ioB | step ioB
    ... | just b | tt₁ = f b
    ... | nothing | A , ioA , ccB = go
    where
    go : IO C
    isDone go = nothing
    proj₁ (step go) = A
    proj₁ (proj₂ (step go)) = ioA
    proj₂ (proj₂ (step go)) a = ccB a >>= f

    _>>_ : {A B} IO A IO B IO B
    ioA >> ioB = ioA >>= \_ ioB

    -- copatterns would be easier to read if they were right-aligned, but whitespace is significant.
    primIO : {A} Prim.IO A IO A
    isDone (primIO {A} ioA) = nothing
    proj₁ (step (primIO {A} ioA)) = A
    proj₁ (proj₂ (step (primIO {A} ioA))) = ioA
    proj₂ (proj₂ (step (primIO {A} ioA))) a = return a


    -- We can implement colists using the same "if then ⊤" trick:
    record Colist (A : Set) : Set where
    coinductive
    @@ -82,11 +57,9 @@ record Colist (A : Set) : Set where
    tail : if null then ⊤ else Colist A
    open Colist


    -- Unfortunately I haven't figured out how to compile the above Colist to Haskell lists, so I can't
    -- implement cat using (getContents >>= putStr∞). Instead, let's import getChar and putChar from
    -- Haskell.

    postulate
    isEOF : Prim.IO Bool
    getChar : Prim.IO Char
    @@ -95,28 +68,16 @@ postulate
    {-# COMPILED getChar getChar #-}
    {-# COMPILED putChar putChar #-}


    -- Constructing a recursive computation using _>>=_ and _>>_ doesn't work because agda cannot see
    -- that the computation will make progress.
    {-# NON_TERMINATING #-}
    non-terminating-cat : IO ⊤
    non-terminating-cat = primIO isEOF >>= \eof
    if eof
    then return tt
    else ( primIO getChar >>= \c
    primIO (putChar c)
    >> non-terminating-cat
    )

    -- Instead, we must inline the outer _>>=_ so that the recursive call is inside a deeper piece.
    -- copatterns would be easier to read if they were right-aligned, but whitespace is significant.
    cat : IO ⊤
    isDone cat = nothing
    proj₁ (step cat) = Bool
    proj₁ (proj₂ (step cat)) = isEOF
    proj₂ (proj₂ (step cat)) true = return tt
    proj₂ (proj₂ (step cat)) false = primIO getChar >>= \c
    primIO (putChar c)
    >> non-terminating-cat
    isDone cat = nothing
    proj₁ (step cat) = Bool
    proj₁ (proj₂ (step cat)) = isEOF
    proj₂ (proj₂ (step cat)) true = return tt
    isDone (proj₂ (proj₂ (step cat)) false) = nothing
    proj₁ (step (proj₂ (proj₂ (step cat)) false)) = Haskell.Unit
    proj₁ (proj₂ (step (proj₂ (proj₂ (step cat)) false))) = Prim._>>=_ getChar putChar
    proj₂ (proj₂ (step (proj₂ (proj₂ (step cat)) false))) _ = cat

    main : Prim.IO ⊤
    main = run cat
  2. @gelisam gelisam created this gist Dec 7, 2016.
    122 changes: 122 additions & 0 deletions Main.agda
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,122 @@
    {-# OPTIONS --copatterns #-}
    module Main where
    -- A simple cat program which echoes stdin back to stdout, but implemented using copatterns
    -- instead of musical notation, as requested by Miëtek Bak (https://twitter.com/mietek/status/806314271747481600).

    open import Data.Nat

    open import Data.Unit
    open import Data.Bool
    open import Data.Char
    open import Data.Maybe
    open import Data.Product
    open import Function
    import Foreign.Haskell as Haskell
    import IO.Primitive as Prim


    -- like ⊤, but Set₁ instead of Set₀
    record ⊤₁ : Set₁ where
    constructor tt₁


    -- The IO module from stdlib is a wrapper around Prim.IO based on musical notation, plus a
    -- NON_TERMINATING wrapper named run. Let's define our own wrapper using copatterns instead.
    --
    -- We cannot define our IO in terms of return and _>>=_. Those are constructors, that is, functions
    -- which construct an IO value from smaller pieces, but when defining codata we must instead specify
    -- the destructors, that is, functions which extract deeper pieces from an IO value.
    record IO (B : Set) : Set₁ where
    coinductive
    field
    isDone : Maybe B -- we either return a value of type B...
    step : if is-just isDone
    then ⊤₁ -- in which case there are no more steps, otherwise...
    else ∃ \A -- there is an intermediate value of type A...
    Prim.IO A -- computed by the first step...
    × (A IO B) -- and followed by a continuation.
    open IO

    {-# NON_TERMINATING #-}
    run : {B} IO B Prim.IO B
    run ioB with isDone ioB | step ioB
    ... | just b | _ = Prim.return b
    ... | nothing | (A , ioA , ccB) = Prim._>>=_ ioA (run ∘ ccB)


    -- To define return, _>>=_, and any other function returning an IO value, we define what each
    -- destructor should return on the resulting IO value.

    return : {A} A IO A
    isDone (return a) = just a
    step (return a) = tt₁

    _>>=_ : {B C} IO B (B IO C) IO C
    _>>=_ {B} {C} ioB f with isDone ioB | step ioB
    ... | just b | tt₁ = f b
    ... | nothing | A , ioA , ccB = go
    where
    go : IO C
    isDone go = nothing
    proj₁ (step go) = A
    proj₁ (proj₂ (step go)) = ioA
    proj₂ (proj₂ (step go)) a = ccB a >>= f

    _>>_ : {A B} IO A IO B IO B
    ioA >> ioB = ioA >>= \_ ioB

    -- copatterns would be easier to read if they were right-aligned, but whitespace is significant.
    primIO : {A} Prim.IO A IO A
    isDone (primIO {A} ioA) = nothing
    proj₁ (step (primIO {A} ioA)) = A
    proj₁ (proj₂ (step (primIO {A} ioA))) = ioA
    proj₂ (proj₂ (step (primIO {A} ioA))) a = return a


    -- We can implement colists using the same "if then ⊤" trick:
    record Colist (A : Set) : Set where
    coinductive
    field
    null : Bool
    head : if null then ⊤ else A
    tail : if null then ⊤ else Colist A
    open Colist


    -- Unfortunately I haven't figured out how to compile the above Colist to Haskell lists, so I can't
    -- implement cat using (getContents >>= putStr∞). Instead, let's import getChar and putChar from
    -- Haskell.

    postulate
    isEOF : Prim.IO Bool
    getChar : Prim.IO Char
    putChar : Char Prim.IO Haskell.Unit
    {-# COMPILED isEOF System.IO.isEOF #-}
    {-# COMPILED getChar getChar #-}
    {-# COMPILED putChar putChar #-}


    -- Constructing a recursive computation using _>>=_ and _>>_ doesn't work because agda cannot see
    -- that the computation will make progress.
    {-# NON_TERMINATING #-}
    non-terminating-cat : IO ⊤
    non-terminating-cat = primIO isEOF >>= \eof
    if eof
    then return tt
    else ( primIO getChar >>= \c
    primIO (putChar c)
    >> non-terminating-cat
    )

    -- Instead, we must inline the outer _>>=_ so that the recursive call is inside a deeper piece.
    cat : IO ⊤
    isDone cat = nothing
    proj₁ (step cat) = Bool
    proj₁ (proj₂ (step cat)) = isEOF
    proj₂ (proj₂ (step cat)) true = return tt
    proj₂ (proj₂ (step cat)) false = primIO getChar >>= \c
    primIO (putChar c)
    >> non-terminating-cat

    main : Prim.IO ⊤
    main = run cat