来自Clean 2.2 手册,第 9 章:
尽管 Clean 纯粹是功能性的,但允许具有副作用的操作(例如 I/O 操作)。为了在不违反语义的情况下实现这一点,经典类型提供了所谓的唯一性属性。如果函数的参数被指示为唯一的,则保证在运行时对应的实际对象是本地的,即没有其他对它的引用。显然,可以安全地执行对这种“唯一对象”的破坏性更新。
具体来说,您可以使Start
通常具有 arity 0(不带参数)的函数从*World
to *World
。这个想法是我们现在有一个改变世界的功能,这意味着允许副作用(它们不再是真正的副作用,而是对世界的操作)。
*
表示类型的唯一性World
。这意味着你永远不能有两个 world 参数的实例。例如,以下将给出编译时唯一性错误:
Start :: *World -> *(*World, *World)
Start w = (w, w)
StdFile
要使用标准IO ,您将需要StdEnv
. 您将需要的功能是:
stdio :: !*World -> *(!*File, !*World)
fclose :: !*File !*World -> !(!Bool, !*World)
我稍微简化了类型,实际上它们来自 class FileSystem
。stdio
从一个世界中打开一个独特 File
的,也返回一个新的、修改过的世界。fclose
关闭世界中的文件,并返回成功标志和修改后的世界。
然后,要从该 stdio 文件读取和写入,您可以使用:
freadline :: !*File -> *(!*String, !*File)
fwrites :: !String !*File -> !*File
freadline
将一行读入一个字符串,包括换行符。fwrites
将字符串写入文件,通常您希望在写入 stdio 时包含换行符。
把它放在一起:
Start :: *World -> *World
Start w
# (io,w) = stdio w // open stdio
# io = fwrites "What is your name?\n" io // ask for name
# (name,io) = freadline io // read in name
# name = name % (0, size name - 2) // remove \n from name
# io = fwrites ("Hello, " +++ name +++ "!\n") io // greet user
# (ok,w) = fclose io w // close stdio
| not ok = abort "Couldn't close stdio" // abort in case of failure
= w // return world from Start
#
语法对您来说可能是新的。它是一种let
允许您对文件(或其他东西)使用相同名称的一种,这比使用更方便,例如:
Start w = w3
where
(io, w1) = stdio w
io1 = fwrites "What is your name?\n" io
(name, io2) = freadline io1
//...
(ok, w3) = fclose io10 w2
现在您应该能够使用辅助函数在伪代码中执行您想要的操作loop :: *File -> *File
,该辅助函数会递归调用自身,直到q
输入。
freadline
除了和之外,还有更多功能fwrites
,请参阅StdFile.dcl
。