# Help Review the new "File Manipulation" tutorial on OCaml.org

**URL:** <https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638>\
**Category:** Community\
**Tags:** documentation, user-feedback, ocamlorg\
**Created:** [July 18, 2023, 9:19am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638 "2023-07-18T09:19:47Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![sabine](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/sabine/32/4147_2.png) [@sabine](https://discuss.ocaml.org/u/sabine)\
**Post date:** [July 18, 2023, 9:19am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/1 "2023-07-18T09:19:47Z")

</div>

Hey everyone,

there’s a new version of the “File Manipulation” tutorial on

[https://staging.ocaml.org/docs/file-manipulation](https://staging.ocaml.org/docs/file-manipulation)

For comparison: the old version of this tutorial is here [File Manipulation · OCaml Tutorials](https://ocaml.org/docs/file-manipulation).

> <https://github.com/ocaml/ocaml.org/pull/1400>

Thanks for taking a look and giving feedback and suggestions for revising this! 🙂

---

<div class="post-metadata">

**Author:** ![chshersh](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/chshersh/32/4964_2.png) [@chshersh](https://discuss.ocaml.org/u/chshersh)\
**Post date:** [July 18, 2023, 4:08pm UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/2 "2023-07-18T16:08:45Z")

</div>

Thanks a lot for writing such tutorials! 🙏

As a person who started learning OCaml only several months ago, I find such articles especially helpful where I can learn OCaml bit-by-bit 💯

Great content. And this version is much better than the previous one! 👏

---

<div class="post-metadata">

**Author:** ![benjamin-thomas](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/benjamin-thomas/32/3966_2.png) [@benjamin-thomas](https://discuss.ocaml.org/u/benjamin-thomas)\
**Post date:** [July 18, 2023, 7:01pm UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/3 "2023-07-18T19:01:10Z")

</div>

It looks very good, great job 👍

It has a “cookbook” feel which I quite like.

Since I know you’re open to suggestions, I thought I’d point out that maybe a dedicated “cookbook” section could make a great addition to the [ocaml.org](http://ocaml.org).

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [July 19, 2023, 8:04am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/4 "2023-07-19T08:04:57Z")

</div>

I’m afraid I don’t have the time to make extensive remarks, as it would amount to a full rewrite of the tutorial. But basically:

1. It is not a good idea to mostly eschew error handling and correct ressource handling (i.e. use of `Fun.finally`). It’s precisely in this kind of tutorial that good habits should be established. There are too many functions here that people will cut and paste which can leak file descriptors including the first `read_from_file` that claims to handle errors or the [second `read_from_file`](https://staging.ocaml.org/docs/file-manipulation#checking-beforehand) which is plain wrong (racy to be precise). If you want to show incorrect usage you should at least put a comment in the code.

2. Don’t promote use of `Sys.file_exists` it’s a broken function which will only lead to head scratchings (or pure anger on a bad day) for both for programmers and end users. It turns permissions errors into `false`. Do a `mkdir bla && touch bla/file.txt && chmod ug-x bla` and enjoy the result of

---

<div class="post-metadata">

**Author:** ![sabine](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/sabine/32/4147_2.png) [@sabine](https://discuss.ocaml.org/u/sabine)\
**Post date:** [July 19, 2023, 8:28am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/5 "2023-07-19T08:28:29Z")

</div>

I’m understanding the overall sentiment here (and from the feedback on the PR itself) as this:

1. cookbook-style presentation of different recipes / things people may want to do is helpful
2. actual code examples show some bad practices - these **must** be fixed to promote good practices

…

Tangent: If `Sys.file_exists` is a broken function…

1. is there any way to fix that in the long-term?
2. Is anyone keeping a list of such “broken Stdlib functions”?
3. Is there a stable package that provides a safer API to the file system?

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [July 19, 2023, 8:34am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/6 "2023-07-19T08:34:49Z")

</div>

> [@sabine](#):
>
> is there any way to fix that in the long-term?

The problem is that currently the function never raises, IIRC it turns all unix errors into `false`, the correct way would be to make it raise with `Sys_error` on unix errors but then maybe some programs rely on the fact that `Sys.file_exists` never raises.

> [@sabine](#):
>
> Is there a stable package that provides a safer API to the file system?

The `Unix` module.

---

<div class="post-metadata">

**Author:** ![sabine](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/sabine/32/4147_2.png) [@sabine](https://discuss.ocaml.org/u/sabine)\
**Post date:** [July 19, 2023, 9:09am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/7 "2023-07-19T09:09:30Z")

</div>

Thank you Daniel, I opened an issue [Stdlib: Make `Sys.file_exists` raise `Sys_error` in error cases, instead of returning `false` · Issue #12393 · ocaml/ocaml · GitHub](https://github.com/ocaml/ocaml/issues/12393)

> The `Unix` module

It looks like Windows support will be improving, and that - in the long term - we need to provide practical-minded documentation that works for both Windows and Unix users.

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [July 19, 2023, 9:33am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/8 "2023-07-19T09:33:58Z")

</div>

> [@sabine](#):
>
> It looks like Windows support will be improving, and that - in the long term - we need to provide practical-minded documentation that works for both Windows and Unix users.

Don’t let yourself be fooled by a name, the `Unix` module is ill-named. It’s a carefully implemented OS abstraction library, emulating most of POSIX functions on Windows (and those that are not are documented on [this page](https://v2.ocaml.org/manual/libunix.html)).

---

<div class="post-metadata">

**Author:** ![sabine](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/sabine/32/4147_2.png) [@sabine](https://discuss.ocaml.org/u/sabine)\
**Post date:** [July 19, 2023, 9:37am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/9 "2023-07-19T09:37:19Z")

</div>

Ah sorry about that… 👍 reminds me of the only two hard things in computer science: naming things and cache invalidation.

---

<div class="post-metadata">

**Author:** ![cvine](https://avatars.discourse-cdn.com/v4/letter/c/90db22/32.png) [@cvine](https://discuss.ocaml.org/u/cvine)\
**Post date:** [July 19, 2023, 7:02pm UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/10 "2023-07-19T19:02:26Z")

</div>

> [@sabine](#):
>
> Is anyone keeping a list of such “broken Stdlib functions”?

If you find there is someone can you ask them to add to the list something I have mentioned before and learnt the hard way, namely that the Unix.execv\* functions in Stdlib are not thread-safe even though we now have domains and have had the Thread module for some time, and this is not documented in the OCaml reference even though users will assume otherwise by analogy with the underlying C functions.

This is not academic. The Lwt authors assumed them thread-safe: Lwt now automatically starts threads by default when encountering potentially blocking operations but uses Unix.execve in its Lwt\_process module. This has the unfortunate feature that it will work under glibc but occasionally blow up under musl.

I am not suggesting that the code should necessarily be rewritten (maybe it can’t be) but it should be documented.

---

<div class="post-metadata">

**Author:** ![arbipher](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/arbipher/32/3544_2.png) [@arbipher](https://discuss.ocaml.org/u/arbipher)\
**Post date:** [July 19, 2023, 7:38pm UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/11 "2023-07-19T19:38:34Z")

</div>

For curious, what is the usual workflow to update or propose a tutorial on [OCaml.org](http://OCaml.org)?

---

<div class="post-metadata">

**Author:** ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)\
**Post date:** [July 19, 2023, 9:11pm UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/12 "2023-07-19T21:11:22Z")

</div>

> [@cvine](#):
>
> Unix.execv\* functions in Stdlib are not thread-safe

Sorry for the naïve question, but can you explain what you mean by thread-safe in this context?

Thanks,  
Nicolas

---

<div class="post-metadata">

**Author:** ![cvine](https://avatars.discourse-cdn.com/v4/letter/c/90db22/32.png) [@cvine](https://discuss.ocaml.org/u/cvine)\
**Post date:** [July 19, 2023, 9:45pm UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/13 "2023-07-19T21:45:52Z")

</div>

By “thread-safe” I mean complies with POSIX and does not give rise to random lockups. (It conditionally applies malloc which is not allowed in the child process of a multi-threaded program.)

---

<div class="post-metadata">

**Author:** ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)\
**Post date:** [July 19, 2023, 10:22pm UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/14 "2023-07-19T22:22:36Z")

</div>

I’m not sure I follow: which child process are we talking about? The result of calling `exec*` is to replace the _current_ process with a different executable, no new process is created. Regardless, reports of thread unsafety in the `unix` library should be reported upstream: [https://github.com/ocaml/issues](https://github.com/ocaml/issues).

Cheers,  
Nicolas

---

<div class="post-metadata">

**Author:** ![cvine](https://avatars.discourse-cdn.com/v4/letter/c/90db22/32.png) [@cvine](https://discuss.ocaml.org/u/cvine)\
**Post date:** [July 19, 2023, 10:59pm UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/15 "2023-07-19T22:59:25Z")

</div>

I also don’t follow. Exec is applied after a fork in any real world program. In what circumstances would a program apply exec except after a fork? Lwt\_process is a typical example. Can you set out the case to which you refer?

---

<div class="post-metadata">

**Author:** ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)\
**Post date:** [July 20, 2023, 4:57am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/16 "2023-07-20T04:57:27Z")

</div>

This is getting off-topic for the present thread, but just to round things up:

> [@cvine](#):
>
> It conditionally applies malloc which is not allowed in the child process of a multi-threaded program.

As mentioned, the issue of `Unix.exec*` allocating memory in multi-threaded programs should be reported upstream so that at least it can be documented.

On a related note, `Unix.create_process` was reimplemented on top of `posix_spawn` precisely to avoid this issue: [https://github.com/ocaml/ocaml/pull/9573](https://github.com/ocaml/ocaml/pull/9573). Even when `posix_spawn` is not available, the fallback code in that function takes care not to allocate in the child process.

Cheers,  
Nicolas

---

<div class="post-metadata">

**Author:** ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)\
**Post date:** [July 20, 2023, 5:08am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/17 "2023-07-20T05:08:44Z")

</div>

> [@nojb](#):
>
> > [@cvine](#):
> >
> > It conditionally applies malloc which is not allowed in the child process of a multi-threaded program.
> 
> As mentioned, the issue of `Unix.exec*` allocating memory in multi-threaded programs should be reported upstream so that at least it can be documented.

I took the liberty of filing a bug report myself: [Unix.create\_process\_env might not be multi-thread safe · Issue #12395 · ocaml/ocaml · GitHub](https://github.com/ocaml/ocaml/issues/12395).

Cheers,  
Nicolas

---

<div class="post-metadata">

**Author:** ![R\_Huxton](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/r_huxton/32/4216_2.png) [@R\_Huxton](https://discuss.ocaml.org/u/R_Huxton)\
**Post date:** [July 20, 2023, 6:58am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/18 "2023-07-20T06:58:03Z")

</div>

My brief notes as an Ocaml learner (but experienced dev).

- “doesn’t return a file descriptor…instead…a channel” - the practical difference being what? Is it just that I can’t open a file in read/write mode?
- There is quite a lot of “chat” before you get to examples / bullet-points. Maybe if you aren’t familiar with reading/writing files in other languages it gives vital context though.
- Start with the “with\_” example, because it is (a) shorter and (b) closes the channel correctly.
- Follow the “with\_” example with the steps it performs _including the try/catch_. This way learners will see why they want to use the short version if they can.
- You need examples of writing/reading a file line-by-line immediately after that - it is likely the most common task a learner would attempt. It is unfortunate that there is no stdlib wrapper to make this less complex, but that example needs to be there.
- No links to the relevant modules in the stdlib docs!
- The examples for “Error Handling” don’t seem to close channels in the event of an exception. If I’ve understood correctly this could leak channels? You might need to move “Remembering to close channels” above this section to give context.
- Maybe a note (near the bottom) about whether garbage-collection closes a channel or not?

HTH

---

<div class="post-metadata">

**Author:** ![ygrek](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/ygrek/32/268_2.png) [@ygrek](https://discuss.ocaml.org/u/ygrek)\
**Post date:** [July 20, 2023, 4:04pm UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/19 "2023-07-20T16:04:08Z")

</div>

I would also mention that writing directly to a final file is the bad pattern, more so if one is overwriting the existing valid file (e.g. some settings modified by user from ui, persistent state, etc) - in case filesystem runs out of space or the unfortunate kernel crash happens in the middle of the write the user is left with the partially written or empty file.  
Proper way to do atomic file (over)writes is to write to the temporary file in the same directory, close, fsync and rename to final path, e.g. [Devkit.Files.save\_as](https://github.com/ahrefs/devkit/blob/master/files.ml#L58)  
And no, this is not a theoretical problem, and actually a widespread mistake in many popular programs (ask me how I know).

---

<div class="post-metadata">

**Author:** ![xavierleroy](https://avatars.discourse-cdn.com/v4/letter/x/a9adbd/32.png) [@xavierleroy](https://discuss.ocaml.org/u/xavierleroy)\
**Post date:** [July 21, 2023, 7:51am UTC](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638/20 "2023-07-21T07:51:04Z")

</div>

> [@cvine](#):
>
> Exec is applied after a fork in any real world program. In what circumstances would a program apply exec except after a fork?

Think of a tail call. A launcher program prepares arguments and environment, then launches another program as its last action. You can find examples in “the real world”, whatever that means.

[Next page](https://discuss.ocaml.org/t/help-review-the-new-file-manipulation-tutorial-on-ocaml-org/12638.md?page=2)
