Skip to content

Conversation

EmileTrotignon
Copy link
Contributor

Right now, with init.ml

let a = "hello"

Running ocaml -init init.ml works; it launches a toplevel where print_endline a works as expected.

However running ocaml -init init.ml -e "print_endline a;;" give Unbound value a.
And ocaml -init doesnotexist.ml -e "print_endline a;;" also gives Unbound value a and no other error.

This PR fixes that behaviour by reading the init argument before running a script.

It is a one line change, but code order is changed in order for the extra function to be available.

@EmileTrotignon
Copy link
Contributor Author

This also fixes ocaml -init a.ml b.ml which was previously broken.

@gasche
Copy link
Member

gasche commented Aug 13, 2025

What happens with .ocamlinit files?

It is a one line change, but code order is changed in order for the extra function to be available.

You could have a first commit that just move the code around, and then a commit for the change.

Note: I notice that -e is not documented in man ocaml, this is something to be fixed as well.

@EmileTrotignon
Copy link
Contributor Author

What happens with .ocamlinit files?
They are always read which is what the doc seems to imply. If its an issue for backwards compatibility its probably possible to introduce a difference between explicitly passing -init and just having a .ocamlinit file.

Another issue I have not been able to untangle this afternoon is that ocaml -blabla a.ml results in an error, while ocaml a.ml -blabla does not.

@EmileTrotignon
Copy link
Contributor Author

The above also means that ocaml a.ml -init b.ml ignores the init file

@kit-ty-kate
Copy link
Member

kit-ty-kate commented Aug 13, 2025

Another issue I have not been able to untangle this afternoon is that ocaml -blabla a.ml results in an error, while ocaml a.ml -blabla does not.

This is because the arguments after the file are expected to be the ones given to the script you're calling:

$ ocaml test.ml -a -b -c
test.ml
-a
-b
-c
$ cat test.ml
let () =
  for i = 0 to Array.length Sys.argv - 1 do
    print_endline Sys.argv.(i);
  done

However, annoyingly it isn't very well documented either. The man page only shows the following syntax:

ocaml [ options ] [ object-files ] [ script-file ]

I think it should probably be something like

ocaml [ options ] [ object-files ] [ script-file [argument...] ]

similar to the syntax from the ocamlrun man page.

@EmileTrotignon
Copy link
Contributor Author

I pushed a commit that makes it so initialisation file lookup is disabled on scripts (including -stdin and -e). Passing -init still works in that commit.

And another commit that introduces -yesinit that allows to still lookup initialization files even when running scripts.

@dra27
Copy link
Member

dra27 commented Sep 5, 2025

My intuition is that the semantics of -yesinit are potentially dangerous (in particular considering it would allow #!/usr/bin/ocaml -yesinit). It's one thing to configure your system to have default startup files for an interactive thing, but I'm not sure that ocaml as a script interpreter should ever be reading, even explicitly?

Regardless, the name doesn't work - the opposite of "no" is not "yes" here (the opposite of the instruction "process no init files" is not "process yes init files", if you see what I mean). Not sure what it ought to be, but perhaps something more like -with-init or even -run-init-scripts-yes-I-realise-it-could-be-bad

@EmileTrotignon
Copy link
Contributor Author

My intuition is that the semantics of -yesinit are potentially dangerous (in particular considering it would allow #!/usr/bin/ocaml -yesinit).

This is not the main thing this PR aims to solve. I included it for the sake of completeness, but I'm happy to remove it.

Regardless, the name doesn't work

I am aware, I did not find anything better, and I thought it was understandable enough and at least a little funny. I thought the proper name would have been -init which is taken. -with-init is good.

@gasche
Copy link
Member

gasche commented Sep 10, 2025

cc @dbuenzli : you have experience running script through the toplevel, and may have valuable feedback on the desirable semantics with respect to initialization files.

@dbuenzli
Copy link
Contributor

dbuenzli commented Sep 10, 2025

When you run scripts you generally want to be able to invoke it regardless of your cwd or personal configuration so I'd just drop that -yesinit which is very brittle. Here is the behaviour I would expect:

  1. If ocaml is run to get an interactive REPL and no -init is specified, the implicit lookup for an rc file is performed ($PWD/.ocamlinit, $HOME/.ocamlinit, $XDG_CONFIG_HOME/ocaml/init.ml).
  2. If ocaml is run to evaluate an expression (-e) or run a script, the implicit lookup is not be performed.
  3. If ocaml is run with -init FILE, FILE is always read and replaces the implicit lookup if any. ocaml should error if FILE does not exist.

It is mostly what ocaml does at the moment except:

  1. That -init is not read if -e is specified, which this PR fixes.
  2. No error is reported if FILE does not exist with -e or script mode, which is bad.

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

Successfully merging this pull request may close these issues.

6 participants