Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No standard input/output #162

Open
laowantong opened this issue Feb 14, 2021 · 16 comments
Open

No standard input/output #162

laowantong opened this issue Feb 14, 2021 · 16 comments

Comments

@laowantong
Copy link

No printing occurs with print_string, print_int, and so on:

image

I would expect an output like under the OCaml REPL:

image

Is this a known limitation, or am I doing something wrong ? In the first case, is there any workaround?

Thanks!

@Naereen
Copy link
Contributor

Naereen commented Feb 14, 2021

Hello there,

I'm not sure if it's a known bug to the authors of ocaml-jupyter, but I did encounter it many times.

Have you tried to also use flush_all() (from Pervasives module, see documentation)?
Or Printf.printf

See these two examples:
Capture d’écran_2021-02-14_16-16-00
Capture d’écran_2021-02-14_16-18-40

It's quite weird, but print_endline forces a flush of the output.

@laowantong
Copy link
Author

Thanks for this neat workaround. So, if I understand correctly:

  • print_endline s immediately prints s.
  • flush_all() prints all the characters of the buffer before the \n character.

image

@Naereen
Copy link
Contributor

Naereen commented Feb 14, 2021

@laowantong yes it appears to be the behavior of jupyter-ocaml.

@laowantong
Copy link
Author

By chance, do you know such a workaround for read_line() ?

image

@Naereen
Copy link
Contributor

Naereen commented Feb 14, 2021

No, I honestly never used interactive inputs from any OCaml programs! Sorry

@laowantong
Copy link
Author

No problem, thanks again for your kind answer!

@laowantong laowantong changed the title No standard output No standard input/output Feb 18, 2021
@akabe
Copy link
Owner

akabe commented Feb 23, 2021

@Naereen Thank you for your answers.
@laowantong read_line cannot be used on jupyter because it's not a console. Please use Jupyter_comm.Stdin.read_line as follows:

スクリーンショット 2021-02-23 10 50 28

https://akabe.github.io/ocaml-jupyter/api/jupyter/Jupyter_comm/Stdin/index.html

@Naereen
Copy link
Contributor

Naereen commented Feb 23, 2021

Could it be possible for OCaml-jupyter kernel to patch such functions, like read_line, which are known to be unsupported functions in Jupyter environment?
It would be helpful to at least print a message saying

Warning: read_line is unsupported on Jupyter, please use [solution]

I actually don't know if OCaml-jupyter kernel has the ability to load a custom .ocamlinit file? (see man ocaml)

INITIALIZATION
When ocaml(1) is invoked, it will read phrases from an initialization file before giving control to the user. The default
file is .ocamlinit in the current directory if it exists, otherwise .ocamlinit in the user's home directory. You can specify
a different initialization file by using the -init file option, and disable initialization files by using the -noinit option.

@akabe
Copy link
Owner

akabe commented Feb 23, 2021

Could it be possible for OCaml-jupyter kernel to patch such functions, like read_line, which are known to be unsupported functions in Jupyter environment?

I have no idea.

I actually don't know if OCaml-jupyter kernel has the ability to load a custom .ocamlinit file? (see man ocaml)

You can specify a custom .ocamlinit file at $(opam config var share)/jupyter/kernel.json generated by ocaml-jupyter-opam-genspec command.

$ cat $(opam config var share)/jupyter/kernel.json
{
  "display_name": "OCaml default",
  "language": "OCaml",
  "argv": [
    "/bin/sh",
    "-c",
    "eval $(opam config env --switch=default) && /Users/aabe/.opam/default/bin/ocaml-jupyter-kernel \"$@\"",
    "ocaml-jupyter-kernel",
    "-init", "/Users/aabe/.ocamlinit",
    "--merlin", "/Users/aabe/.opam/default/bin/ocamlmerlin",
    "--verbosity", "app",
    "--connection-file", "{connection_file}"
  ]
}

Re-run jupyter kernelspec install [ --user ] --name ocaml-jupyter "$(opam var share)/jupyter" after editing a value for -init option.

@laowantong
Copy link
Author

Awesome! Thank you @akabe for this workaround which works perfectly. I use Jupyter Notebook as an interactive environment in my FP course, and these IO functions are a must for programming games.

@Naereen
Copy link
Contributor

Naereen commented Feb 23, 2021

@laowantong, great if that solves the issue for you.

But are you really planning to develop a game from Jupyter notebook? Using this Jupyter_comm.Stdin.read_line for a game will imply that the game cannot be played outside of Jupyter.

Have you had a look at https://github.com/mahsu/MariOCaml ? It's compiled to Javascript using js_of_ocaml (standard tool, it also provided the compiler interpreter at https://betterocaml.ml/editor/ or https://try.ocamlpro.com/.
The MariOCaml shows what can be done in pure OCaml when you know the game frontend will be a HTML canvas.

@laowantong
Copy link
Author

laowantong commented Feb 23, 2021

@Naereen In fact, I do not call directly this function, but define a mutable variable with a default of:

let read = ref read_line;

This works under console. Under Jupyter Notebook, I mutate it:

read := (function () -> Jupyter_comm.Stdin.read_line "")

And for the tests, I use a stream:

read := (let x = Stream.of_list ["-10"; "500"; "10"] in function () -> Stream.next x);

This is a simple text game (dominoes), nothing as fancy as Mario. Nevertheless, thanks for the links !

@Naereen
Copy link
Contributor

Naereen commented Feb 23, 2021

Interesting!
I don't know if it's possible to dynamically detect if the code is running inside Jupyter or not...

In Python/IPython, it's easy:

import IPython
def is_in_notebook(): return IPython.get_ipython() is not None

If this is possible in OCaml, it could be used to automatically change your read function.

@akabe
Copy link
Owner

akabe commented Mar 7, 2021

You can check whether a repl is on jupyter or not, by:

Filename.basename Sys.argv.(0) = "ocaml-jupyter-kernel"

This is not perfect, but I think enough in practical cases.
Or you give the following .ocamlinit file (different from one for a standard repl):

module Sys = struct
  include Sys
  let read () = () [@@ocaml.deprecated "Use Jupyter_comm.Stdin.read_line instead"]
end

I don't recommend replace Sys.read with Jupyter_comm.Stdin.read_line because they have different types.

@Naereen
Copy link
Contributor

Naereen commented Mar 7, 2021

Oh great for the hack on Filename.basename, that's smart!

For the [@@ocaml.deprecated "..."] hack, I saw that in recent versions of the OCaml codebase, but I guess it doesn't work on older versions, I'm under 4.05.0 here and it does nothing special...

@terencode
Copy link

terencode commented May 1, 2021

To be able to #use "topfind", I had to first do let () = Topdirs.dir_directory (Sys.getenv "OCAML_TOPLEVEL_PATH")

My OCAML_TOPLEVEL_PATH is /home/terence/.opam/default/lib/toplevel.

I see the kernel.json exports this env by running (opam config env --switch=default --shell=sh).

Is it normal I still need to use the Topdirs.dir_directory function?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants