0

How to do this in Clean?

Pseudo code:

loop:
    input = read_stdin
    if input == "q":
        break loop
    else:
        print "you input: ", input

Actually, I have had a glance at some pdf. But I got an imagination, It's difficult to deal with stdin and stdout. Could I have a code example to use stdio?

Following Keelan's instructions, I had finished my little program.

module std_in_out_loop
import StdEnv

loop :: *File -> *File
loop io
# io = fwrites "input your name: " io
# ( name, io ) = freadline io
# name = name % ( 0, size name - 2 )
| name == "q"
# io = fwrites "Bye!\n" io
= io
| name == ""
# io = fwrites "What's your name?\n" io
= loop io
| otherwise
# io = fwrites ( "hello " +++ name +++ "\n" ) io
= loop io

Start:: *World -> *World
Start world
# ( io, world ) = stdio world
# io = loop io
# ( ok, world ) = fclose io world
| not ok = abort "Cannot close io.\n"
| otherwise = world
sa ma
  • 135
  • 7

1 Answers1

1

From the Clean 2.2 manual, chapter 9:

Although Clean is purely functional, operations with side-effects (I/O operations, for instance) are permitted. To achieve this without violating the semantics, the classical types are supplied with so called uniqueness attributes. If an argument of a function is indicated as unique, it is guaranteed that at run-time the corresponding actual object is local, i.e. there are no other references to it. Clearly, a destructive update of such a “unique object” can be performed safely.

Concretely, you can make Start, which normally has arity 0 (takes no arguments), a function from *World to *World. The idea is that we now have a function that changes the world, which means that side effects are allowed (they're not really side effects any more, but operations on the world).

The * indicates the uniqueness of the World type. This means that you cannot ever have two instances of the world argument. For example, the following will give a compile-time uniqueness error:

Start :: *World -> *(*World, *World)
Start w = (w, w)

To use standard IO, you will need functions from the StdFile module in StdEnv. The functions you're going to need are:

stdio :: !*World -> *(!*File, !*World)
fclose :: !*File !*World -> !(!Bool, !*World)

I simplified the types a bit, actually they're from the class FileSystem. stdio opens a unique File from a world and also returns the new, modified world. fclose closes a file in a world, and returns a success flag and the modified world.

Then, to read and write from that stdio file, you can use:

freadline :: !*File -> *(!*String, !*File)
fwrites :: !String !*File -> !*File

freadline reads a line into a String, including the newline character. fwrites writes a string to a file, usually you want to include a newline character when writing to stdio.

Putting it together:

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

The # syntax might be new to you. It's a kind of let which allows you to use the same name for files (or other things), which is more convenient than using, e.g.:

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

Now you should be able to do what you want in pseudocode using a helper function loop :: *File -> *File, which calls itself recursively until q is inputted.

There are more functions than only freadline and fwrites, see StdFile.dcl for an idea.

  • Hi, Camil, Glad to See you again. Would you mind? give me an icl file? which is equivalent to the Pseudo code. Let me play with it ? And then, I'll come back to read your instructions. It may stupid? I don't know? Please. Thanks! – sa ma Mar 25 '16 at 09:26
  • @sama the thing after 'Putting it together' is a ready example, just add a module header and `import StdEnv`. Try to write `loop :: *File -> *File` yourself, as I suggested in the last paragraph. If you don't manage, please ask a new question including your code and a concrete question. Thanks! –  Mar 25 '16 at 09:37
  • Hi, Camil! Thanks Again. For me? CLEAN is very different, **Start** is the only one chance to output something? (other language can do that everywhere. Example, for python? If Yoy want to read something? or output something? it is very easy. I can google that, and write some code for myself ) But, It is diffcult for me. Sorry, I have no idea to group them into an icl file. – sa ma Mar 25 '16 at 10:02
  • @sama yes, IO in a functional language is a little different. However, it is not true that Start is the only chance to output something. Again, the one code block under "Putting it together" above is a fully working example. If this is too difficult and you need more explanation, see [Functional Programming in Clean, chapter 5](http://www.inf.ufsc.br/~jbosco/cleanBookI.pdf) –  Mar 25 '16 at 10:05
  • Hi! Camil! It is Not True? I play whith that, **Putting it together** It only read stdin once, and ouput once. Not a loop? ( I copy that, and press F5) – sa ma Mar 25 '16 at 10:40