module interactiveProgramsAgdaUnsized where

open import NativeIO
open import Function


record IOInterface : Set₁ where
  field  Command   :  Set
         Response  :  (c : Command)  Set

open IOInterface public

mutual
  record IO (I : IOInterface)  (A : Set) : Set where
     coinductive
     constructor delay
     field force : IO' I A

  data IO' (I : IOInterface)  (A : Set) : Set where
    do'     : (c : Command I) (f : Response I c  IO I A)   IO' I A
    return' : (a : A)                                        IO' I A

open IO public

module _ {I : IOInterface} (let C = Command I) (let R = Response I) where
  do  : ∀{A} (c : C) (f : R c  IO I A)  IO I A
  force (do c f) = do' c f

  return : ∀{A} (a : A)  IO I A
  force (return a) = return' a

  infixl 2 _>>=_

  _>>=_ :  ∀{A B} (m : IO I A) (k : A  IO I B)  IO I B
  force (m >>= k) with force m
  ... | do' c f   = do' c λ x  f x >>= k
  ... | return' a = force (k a)


  {-# NON_TERMINATING #-}
  translateIO :  {A} (tr : (c : C)  NativeIO (R c))  IO I A  NativeIO A
  translateIO tr m = case (force m) of λ
     {  (do' c f)      (tr c) native>>= λ r  translateIO tr (f r)
     ;  (return' a)    nativeReturn a
     }


data ConsoleCommand : Set where
  getLine   :  ConsoleCommand
  putStrLn  :  String  ConsoleCommand

ConsoleResponse : ConsoleCommand  Set
ConsoleResponse  getLine      =  String
ConsoleResponse (putStrLn s)  =  Unit

ConsoleInterface : IOInterface
Command ConsoleInterface  = ConsoleCommand
Response ConsoleInterface = ConsoleResponse


IOConsole : Set  Set
IOConsole = IO ConsoleInterface

translateIOConsoleLocal : (c : ConsoleCommand)  NativeIO (ConsoleResponse c)
translateIOConsoleLocal (putStrLn s)  =  nativePutStrLn s
translateIOConsoleLocal getLine       =  nativeGetLine

translateIOConsole : {A : Set}  IOConsole A  NativeIO A
translateIOConsole = translateIO translateIOConsoleLocal


{-# NON_TERMINATING #-}
cat   :  IOConsole Unit
force cat =  do' getLine λ{ line  
             do (putStrLn line)   λ _ 
             cat
             }

mutual
  cat' :  IOConsole Unit
  force cat' =  do' getLine λ{ line  
                cat'' line}

  cat'' : String  IOConsole Unit
  force (cat'' line) = do' (putStrLn line)   λ _ 
                       cat'                                


main : NativeIO Unit
main = translateIOConsole cat