Before you give up on this line of investigation, let’s figure out what went wrong? These sorts of linkage problems are almost always due to environment variables and such – not something deeply wrong.
In Error: Library "netcgi2" not found you are mixing packages from two different package managers and expecting things to work. And then you report error messages without showing proper context to help you debug them… and get confusing feedback as a result. There is nothing particular wrong about this! We all get confused with stuff, and it is easy to go in wrong directions when we are confused. But I would not draw too much conclusions from that either, I think the same can occur in the other language ecosystems you mentioned.
Indeed, this is 100% correct. With Rust and Python both, you have ensure that your search-paths and other environment variables are set correctly, or linkage and runtime errors will result. I’ve encountered this many times with things like
pyscf and a few other packages.
Funny how Base overrides stdlib so that the stdlib doc is no longer relevant. Makes for awkward googling sessions indeed…
Ah I forgot about modular implicits! Would be great if they land in the language, it would make OCaml more newcomer friendly I think.
I had a simple task to be done: getting a rss feed, selecting what I hasn’t been already forwarded, and send a mail to make me aware of the news (typically a feed with one news per month). The program is intended to be launched by cron.
I tried with Ocaml… first, the cohttp library failed at getting my feed (TLS error). I finally gave up and use the system command to launch wget. Not that satisfying. The XML parsing with Ezxmlm was very handy. The Sqlite database access was nicely done with Caqti and ppx_raper. But finally I didn’t manage to make Letters send a mail. It just blocks.
Then I started my work in Python… the job was done quite quickly, but I miss the type checking at the compile time. The library choices are quite straightforward (on OCaml, there are often many competing projects and we can get lost… ).
Then I guess I will still have a Python preference, not for the language itself, but for the available libraries. It is a bit a pity.
With a bit of time, I will perhaps try to debug the whole thing, but I add preferred to have my job done quickly.
Frederic, by some chance would you be willing to share that code (the Python version, and perhaps the OCaml version)? I’m curious, but also, kind of thinking about trying to get the OCaml version working.
It’s all @ninjaaron 's fault: I have renewed drive to use OCaml for scripts, so hey, you have a problem that seems like it ought to be straightforward, why not give it a try?
ETA: FWIW, I find the automatic decision to write all systems-interacting libraries with lwt (or some other monad) to be painful. [I can deal with the `result` monad, it isn’t awful] It seems like people have forgotten that there are threads, and that unless you’re actually dealing with high concurrency, threads are enough, even with a global interpreter lock. Ah, well.
ETA2: when I wrote some code that needed to interact with an HTTPS webservice, I used
ocamlnet, which I found quite nice, again FWIW.
FWIW I sneaked in some type checking in a python project via
mypy. However optional type checking will only go so far, there are still plenty of holes and things that are unchecked, and I very much prefer compile-time type checking that OCaml provides.
I like Lwt, and have used it a lot in the past, and there is a nice ecosystem around it. OTOH I’m working on a quite old project now that still spawns one thread per incoming API call. I’ve been thinking about modernizing it and moving it to Lwt. However I really like the ease of debugging with stacktraces and threads, and if I wait just a little bit longer I may get fibres and effect handlers. Need to actually try
eio, introducing it may be less invasive than a monad, and could potentially be introduced incrementally whereas Lwt is more an everything or nothing approach.
[Kind of reminds me of getting internet at home, for a long time in highschool I had none (while friends were getting dialup or ADSL internet). Then once fiber became available I immediately got it at home, while everyone else was still using (A)DSL If you stay on an old technology long enough, you might be able to upgrade to the newest by skipping intermediate steps ]
ocamlnet → ocaml < 4.00.0. It was a nice project for its time though. Then Lwt came along and I attempted to gradually move a project by running both an Equeue engine and and Lwt engine in the same process and getting Lwt monads talking to equeue engines… not the greatest choice, should’ve went with replacing everything with Lwt in one go…
If you managed to construct an email perhaps you could send it with another tool until whatever bug you found in
letters gets fixed (e.g. shelling out to
If you’re inside
cron you wouldn’t even need to do that, you could attempt to take advantage of its automatic send-an-email feature, all you have to do is write to standard output channels and define
MAILTO, see crontab(5) - Linux manual page
ocamlnet, I use it with Ocaml 4.14.0 no problem. To what do you refer ?
Re: lwt, the problem is, it’s its own little language. You have to learn to do everything from-scratch, and this is true of all monads. I use the
result monad and I don’t even know the cromulent idiom for consuming the error at the end of some sequence of operations.
Whereas with threads, we all know how to do this, b/c we’ve been doing it forever. It doesn’t help to argue that “well we had to learn how to use threads once”: every systems jock knows how, and he can’t just forget it, b/c he’ll eventually have to go back to C++ or Python for something.
Ah maybe the solver on OCaml 5.0 just printed the wrong constraint, indeed it still installs on 4.14
Yes, I only ever understood monads once I started writing some actual code with Lwt to see how it works, and then suddenly it all started to make sense. All the documentation and monad tutorials can’t replace actually trying it out (and stumbling a couple of times along the way until it “clicks”). And then for a moment I thought I should write down what I learned in a tutorial, but that won’t help the next person, unless they actually try it out and understand how it works.
FWIW the future/promises terminology in the latest Lwt docs are a lot better than the ‘lightweight threads’ initial terminology.
Zig has an interesting and easy to understand approach here: What is Zig's “Colorblind” Async/Await? | Loris Cro's Blog, and I’m hoping that we’ll get something similar with OCaml and effects, and/or ‘domainslibs’/‘eio’.
Explaining what ‘async’ / ‘await’ does is a lot simpler than explaining what a monad is or how to use it, and it doesn’t require changing and wrapping the types of your entire application into a monad (I can see why Haskell requires that, but OCaml doesn’t have to).
So for me, it isn’t that I don’t understand monads: I mean, I completely understand what’s going on – it’s a CPS or SPS translation, and more generally it’s a standard semantics-based translation. Sure, I know how that works, and I know how to read the code and understand it. What I fail to understand, is the idioms one should be using. Let me give an example: here’s some code to process the contents of a file
f, write it to
f.NEW, then rename
f.bak and rename
f. At every step, errors could happen, so I use the
result monad with
bos to do the job. It looks perfectly pretty
So: how do I actually do a try-catch around a call to
fixin1 so I can print out the error-message. I mean, yes I know how to do this in raw OCaml code – after all, I know what the monad’s operators expand to, so I can work with that. But what’s the idiomatic way to do it?
That’s explained nowhere. And this is what really, really pisses me off about these damned monads: they are the right-hand-sides of standard semantics equations, but nobody explains all the equations – specifically in the case of the
result monad, nobody explains the equation for try-catch.
ETA: Oops, forgot the code.
let fixin1 f = let open Fpath in let f = v f in let newf = add_ext "NEW" f in let bakf = add_ext "bak" f in let* st = OS.Path.stat f in let mode = st.Unix.st_perm in let* contents = read_fully f in let* contents = fixin_contents ~f contents in let* () = write_fully ~mode newf contents in let* () = OS.Path.move ~force:true f bakf in let* () = OS.Path.move ~force:true newf f in Ok ()
Setting up a productive OCaml environment (still in progress) has the most painful of them all. With Java/Kotlin/Rust/Go/JS/TS, the process is: open it up in IntelliJ, everything works. Jump to def. Jump to usage. Open a window with all Structs/Classes, start typing name to filter, to select. Rename all usage, etc …
With Clojure, it’s: Emacs + Cider.
With OCaml, I’m still trying to figure out the “right” VSCode (or VIM) + Dune + Utop combo.
Minor things not quite working in OCaml yet:
- easily running a single embedded unit test
- when typing untyped code, having inferred types show up in light blue in the IDE
- open up a list of all functions / structs / modules, start typing characters to filter the list
- editor-repl integration (on the level of Emacs/Clojure’s C-x C-e)
I propose tou to go in the Index of /~loyer/ocaml directory. The
check_rss0.ml was my tentative to get a rss.xml file.
check_rss.ml use a
wget subcommand. But blocks when trying to send the mail.
check_rss.py is a working version which does the job.
Those threads are a mix of truly brilliant feedback, mixed with OCaml doesn’t have X feature that I have in Y language and complains about stuff that it’s fixed already/corner case of a library.
If we ever care about adoption, features missing or any other global feedback the survey would be a brilliant place to gather enough data and avoid having those non-constructive threads that goes nowhere, the only valid response to those are: “Good feedback, please submit those into ”.
As a group of people who care about a particular language, and use the rational part of our brains to work I expect to do better than this.
@davesnx indeeed, this thread is in dire need of someone summarizing and structuring the feedback, if we want to be able to turn it into actionable items. Would you be willing to be that person?
There are some useful combinators in this library:R (rresult.Rresult.R)
If you want to catch the error and add more information to it then use: reword_error or reword_error_msg
and supply a function that extends the error with more info (e.g. the filename you were handling, the high-level function you were in). On the OK path your function won’t be called.
To print the message you could use Logs.on_error and supply the error handler in
If you want to do it without using any external libraries, then Result in the standard library has some useful
Result.fold (where you can supply an ~error function).
If you just want to print and otherwise let the execution flow continue then you can use
E.g. I sometimes use a function like this for debugging:
let observe_error t = Result.iter_error (fun err -> (* ... log the error ... *) ) t; t
And it can be used like this:
let* fixin1_ok = fixin1 f |> observe_error in
Did you try following the instructions on the ‘Get Up and Running’ page on the OCaml website? If those instructions didn’t work, did you try filing an issue?
VSCode supports #2 (type hints), but does it support #1, #3, #4? If not, what are you suggesting ?
Confirming: When you or someone you know has a bad experience with some content on the OCaml website, we do want to know about that, so we can fix it.
Everyone is also very welcome to open PRs to improve the texts. All the tutorials in the “Learn” section of the OCaml website are generated from the markdown files here: ocaml.org/data/tutorials at main · ocaml/ocaml.org · GitHub. Even if you’re just changing one line of text or code in a way that would have helped your former self struggle less: it is a valuable contribution that probably has a greater impact than you expect.