Problems with standard input

I’m a bit frustrated by this :frowning:

I am simply trying to ask for input from the user and running into a lot of problems, but seemingly only when I am using goblins.

Please see gib-gab-gob/actors.scm at main - gib-gab-gob - Gitea: Git with a cup of tea for some context, specifically around the prompt->move function.

If I extract prompt->move to its own function it appears to work exactly as intended, but as soon as I run this code inside a vat/Goblins context I start to run into weird issues where the standard input from (current-input-port) is invalid.

If there is a better way I would like to know, but it seems like a simple thing so I am confused why it would not be working.

When I run this in a repl I get output that is inconsistent, but sometimes it’s like this:

enter move? > 0 1
move is ()
coords are ()

sometimes just this!

enter move? > 0 1
move is (1)

Is there some weird interaction between Goblins and the standard input port?

(define (prompt->move)
    (format #t "enter move? > ")
    (map string->number (string-tokenize
       (read-line (current-input-port))

Hi vivi,

I don’t know exactly what’s going on, but it could be that the dynamic state current-input-port within the fiber is not the same as the main thread, thus explaining why reading input isn’t working. This problem touches upon another issue, as well: It’s not a good idea to block the vat by waiting for user input since it will stop all message processing. It’s best to do I/O outside of the vat and pass the results in. We have a couple ways of doing this and one or both should hopefully resolve your main issue.

Since prompt->move appears to work fine outside of the vat context, the simplest option may be to wait for user input in the main thread and pass the results as messages into the vat.

(define a-vat (spawn-vat))
(define alice (spawn ^mover))
(while #t
  (define move (prompt->move))
  ($ alice 'move move))

This loop is pretty silly but I hope it illustrates the basic idea. This approach has the benefit of decoupling the I/O layer from the model.

Another approach is to use spawn-fibrous-vow, a handy but as-of-yet undocumented procedure in the (goblins vat) module. This allows arbitrary code to be evaluated outside of the vat and the results returned in the form of a promise:

(with-vat a-vat
  (on (spawn-fibrous-vow (lambda () (prompt->move)))
      (lambda (move)
        ($ alice 'move move))))

The thing I’m not sure of at the moment is if the current input port will be set to the right thing. Seems like it may not be, given the issues you are having. In which case, you could capture the main thread’s current input port and pass it in as necessary.

(define input-port (current-input-port))
(with-vat a-vat
  (on (spawn-fibrous-vow (lambda () (prompt->move input-port)))
      (lambda (move)
        ($ alice 'move move))))

In the above example I adjusted prompt->move to take a port as an argument.

Hope this helps!

1 Like

Thank you, I appreciate it! This makes complete sense. I will give this a shot when I get a chance tomorrow, and hopefully it gets me back into the flow. I also plan to make another blog post about this which might help other new users.