Agda:将一行标准输入读取为String而不是Costring

时间:2014-02-16 06:44:11

标签: io agda

我正在尝试编写一个简单的程序,从标准输入读取一行,将其反转,然后打印反转的字符串。

不幸的是,原生getLine函数会读取Costring;我只能反转String s;并且没有将Costring带到String的功能。

如何修改此程序进行编译?

module EchoInputReverse where

-- Agda standard library 0.7
open import Data.List using (reverse)
open import Data.String
open import Foreign.Haskell using (Unit)
open import IO.Primitive

postulate
  getLine : IO Costring

{-# COMPILED getLine getLine #-}

main : IO Unit
main = 
  getLine >>= (λ s → 
  -- NOTE: Need a (toString : Costring → String) here. Or some other strategy.
  return (toCostring (fromList (reverse (toList (toString s))))) >>= (λ s' → 
  putStrLn s'))

2 个答案:

答案 0 :(得分:10)

你不能这样做,至少不是直接的。问题是Costring的大小可能是无限的,而String s必须是有限的。

想象一下将程序作为prog < /dev/zero运行,会发生什么? reverse函数只有在到达输入列表的末尾后才能生成第一个元素,并且可能永远不会发生。

我们需要表达的是,将Costring转换为String可能会失败。一种方法是使用偏好monad。我们来看看定义:

data _⊥ {a} (A : Set a) : Set a where
  now   : (x : A) → A ⊥
  later : (x : ∞ (A ⊥)) → A ⊥

因此,我们可以使用A类型now类型的值,或者我们需要等待later。但请注意符号:这意味着我们实际上可以永远等待(因为可以有无数个later构造函数)。

我将转换和反转合并为一个函数。首先进口:

open import Category.Monad.Partiality
open import Coinduction
open import Data.Char
open import Data.Colist
  using ([]; _∷_)
open import Data.List
  using ([]; _∷_; List)
open import Data.String
open import Data.Unit
open import IO

现在,reverse函数的类型应该提到我们将Costring作为输入,但返回String可能会失败。然后实现相当简单,它通常与累加器反向:

reverse : Costring → String ⊥
reverse = go []
  where
  go : List Char → Costring → String ⊥
  go acc []       = now (fromList acc)
  go acc (x ∷ xs) = later (♯ go (x ∷ acc) (♭ xs))

但是,我们可以打印String,但不打印String ⊥!这就是IO有用的地方:我们可以将later构造函数解释为“什么都不做”,如果我们找到now构造函数,我们可以putStrLn它包含String

putStrLn⊥ : String ⊥ → IO ⊤
putStrLn⊥ (now   s) = putStrLn s
putStrLn⊥ (later s) = ♯ return tt >> ♯ putStrLn⊥ (♭ s)

请注意,我使用IO模块中的IO,而不是IO.Primitive中的getLine模块。这基本上是建立在假设上的一层,所以它更好一些。但是如果你想使用import IO.Primitive as Prim postulate primGetLine : Prim.IO Costring {-# COMPILED primGetLine getLine #-} getLine : IO Costring getLine = lift primGetLine ,你必须写:

main

最后,我们可以编写main = run (♯ getLine >>= λ c → ♯ putStrLn⊥ (reverse c)) 函数:

C-c C-x C-c

使用$ cat test hello world $ prog < test dlrow olleh 编译此程序然后运行它,我们得到了预期的结果:

{{1}}

答案 1 :(得分:5)

#agda上的Saizan指出,可以假设getLine : IO String代替getLine : IO Costring。这有效。所以你得到:

module EchoInputReverse where

-- Agda standard library 0.7
open import Data.List using (reverse)
open import Data.String
open import Foreign.Haskell using (Unit)
open import IO.Primitive

postulate
  getLine : IO String

{-# COMPILED getLine getLine #-}

main : IO Unit
main = 
  getLine >>= (λ s → 
  return (toCostring (fromList (reverse (toList s)))) >>= (λ s' → 
  putStrLn s'))

缺点是这种方法断言getLine总是返回一个有限字符串,这在prog < /dev/zero的情况下可能不正确,因为@Vitus指出。< / p>

但我不认为这很重要。如果getLine实际上确实返回一个无限字符串,则此解决方案和@ Vitus的解决方案都不会产生终止的程序。他们有相同的行为。

在这种情况下检测输入是否无限并产生错误将是理想的。但是这种无穷大的检测通常是不可能的。