What are the biggest reasons newcomers give up on OCaml?

Because I learned OCaml for work, I didn’t have the luxury of giving up. Also, I was excited to learn OCaml because of an experience in a university course I took some years earlier, so I also had determination.

So, I’m not sure if this is something that would have otherwise tempted me to give up, but I found myself repeating myself a lot in OCaml.

One thing that I found myself doing a lot was having failwith cases that I was sure couldn’t happen because I’d already checked those conditions previously. But it was too much trouble to create a new type and a new function to “narrow” the old type to the new type.

I didn’t know, yet, that OCaml has “type narrowing”. I wouldn’t have even known that term except that it featured prominently in the documentation of Mypy, the Python optional typechecker. And one day, it just occurred to me to do a web search to see if there was a way to do that in OCaml.

Another thing I feel might be useful is ad-hoc polymorphism (like traits in Rust or templates or “concepts” in C++). I know from reading elsewhere on this forum that it is controversial, though. Also, I’ve understood that something like that can be done with modules, but I haven’t tried it, yet.

I don’t have a ready example of where it would help me, though, because its been a while since I was working on the OCaml parts of my projects.

I’ve seen elsewhere on this forum concerns raised about the debugger. I thought the debugger was pretty good, and it is clever to be able to step backward in code, but it took some getting used to how to set breakpoints. I think I remember it giving some kind of error about threads, and I had to upgrade to 4.14, then it gave some error(s) (or warning(s)) but still worked. Also, it wasn’t integrated with the IDE (VS Code), except for old versions, but I understand that is being worked on now. I suppose the concerns were targeted at being able to use the debugger on native code or for multicore code.

Also, I had to use ppx_deriving in order to print some things that would be easy to print in other languages.

OCaml syntax took more getting used to than I expected. I think it is because I had to get used to when a form of expression indicates that a function is being applied, and I had to get used to the compiler’s error messages. The Reason syntax might help beginners with that. I haven’t tried it, but now I think I would prefer the standard OCaml syntax, and I think that currying is a really clever and elegant feature.

2 Likes

Also, the documentation for the debugger said (and maybe still says) that it doesn’t work under Windows, but I found that it does work, at least within the OCaml MinGW Cygwin environment.

The discussion veered off-topic for a moment, but I couldn’t resist mentioning word or long long types in C…

My preferred solution to this problem is Rust, which has explicitly sized types (i8, u8, i64, etc.), and notably a char that represents a ‘Unicode scalar value’.

However, I find the idea of replacing char with byte a bit absurd—neither is an ideal name for an unsigned 8-bit integer. In reality, the size of a “byte” can be hardware-dependent. As far as I know, in OCaml, the size of char is the same as int.

Note: In C, the char type can be signed or unsigned depending on the compiler. It is signed by default in GCC, unless the -funsigned-char flag is specified. In OCaml, char is always unsigned, which, in my opinion, is a more reasonable behavior.

Edit: This concerns documentation. In the context of issue #12547, it has come to my attention that some functions are indeed misleadingly documented and can be confusing for newcomers. The documentation should specify “read exactly [len] bytes” instead of “read exactly [len] characters”.

Are there any resources on how to split a large module into a directory of sub-modules? Something like splitting lib/module.ml into lib/module/mod1.ml, lib/module/mod2.ml, etc. . I tried this but realized I couldn’t access other modules in the library within these sub-modules and they were also not accessible from other module in the lib/ directory. I tried googling but did not find any material on how this can be done.

1 Like

If you are using dune, you can have a look at include_subdirs — Dune documentation

1 Like

Note that if you only want one level then you don’t even need to mess with subdirs settings. If you have a bin/main.ml like this:

let () = 
    print_endline "Hello, World!";
    print_endline Xyzlib.hello;
    print_endline Xyzlib.Alpha.hello;
    print_endline Xyzlib.Beta.hello

And a directory like this:

xyzlib/
├── alpha.ml
├── beta.ml
├── dune
└── xyzlib.ml

The dune-file doesn’t need alteration:

(library
 (name xyzlib))

You do need to explicitly re-export the sub-modules from the top-level xyzlib.ml (or mli):

module Alpha = Alpha
module Beta = Beta
let hello = "hello from top level Xyzlib"

But alpha.ml / beta.ml don’t need to do anything special:

let hello = "Hello from alpha"
let hello = "Hello from beta"

The thing that caught me out at first was that:

  • If you don’t have an xyzlib/xyzlib.ml dune will effectively create one for you, re-exporting all the sub-modules
  • If you do have xyzlib/xyzlib.ml then you have control over what you export but have to remember to do it yourself.

I believe this behaviour is a dune thing rather than something from ocaml itself. If you are using an alternative build system you probably need to handle it differently.

1 Like

Is there a pattern out there that I can use to avoid type checking errors when chaining monads inside a function that returns a (unit, [> some_error]) result type? When using the let* syntax to chain Result monads I keep getting type checking errors like This expression has type unit but an expression was expected of type ('a, [> bunch_of_polymorphic_errors]) result . I think it’s triggered by one of the monadic calls that returns a ('a, [> bunch_of_polymorphic_errors]) result type.

Stumbled over this …
That is old K&R-style that is absolutely digusting in my eyes. #include <stdint.h> and then switch to uint8_t, int16_t, char, wchar etc. That is clear, well defined and works over platforms. Have a look at C99. Burn that K&R-book or sell it to your worst enemy. :slight_smile:

What my biggest problem with OCaml is, are the description of the libraries and their functions. They are very abstarct (at least for a newcomer). They would be way more helpful when examples are included. At least, that’s the way I understand best.

2 Likes

The let* operator for the result type is an alias for Result.bind. The body of the let* expression (the code after and within the scope of let* x = ... in) forms the body of the function to which bind is applied. It must therefore evaluate to a result type (and not the unit type as reported in your error message).

One answer to your question may be to have your let* expression evaluate to Ok () instead of () in case of success. However maybe at this point in your code you don’t need to be in monad-land at all and you should match on the result type earlier in your code so as to deal with the relevant failure case.

1 Like

Thanks, I was able to get rid of the errors by restructuring the code and using pattern matching in cases where a monad wasn’t needed.

I’m afraid I get:

File "lib/dune", line 5, characters 2-17:
5 |  (include_subdirs unqualified)
      ^^^^^^^^^^^^^^^
Error: Unknown field include_subdirs

Is this meant to go inside a different dune file other than the one in the lib directory?

EDIT: Nevermind, it looks like I have to define it outside of the library stanza

I think we can do this one in a new thread.

2 Likes

Fair enough. I try not to create new threads just in case my question is a duplicate. But here you go, I create one for this question: How to create a new file while automatically creating any intermediate directories?

Hello, there! Let’s please not use this thread for general technical help. Having a running thread for people to discuss the topic here is quite useful, IMO, but having off topic technical questions makes it much noisier.

Technical questions are very welcome in the forum, and there’s nothing wrong with creating new threads to ask them! :slight_smile:

The best way to address this is to search first, but if you don’t find an obvious fit there is no problem with having a bit of accidental overlap, imo. Posting an unrelated thread doesn’t help with tho.

Thanks!

3 Likes

The question might be a little mal-formed, inasmuch as some people who were put off by Ocaml never really started with it, so they didn’t properly speaking “give up.”

Syntax? There are practical problems with it, that come from using “;” the way Ocaml does. For example at the last clause of a match, where it’s ambiguous to casual users. There are esthetic problems, which are not a trivial matter. Is Ocaml code as legible as it could be? I hesitated to make this kind of criticism, because it’s baked into the language and not something that can be fixed – but wait, it was fixed, back in the day when there was a preprocessor option for an alternate syntax.

And then there’s the preprocessor or whatever you call it, that people use to get some of the features Haskell has like derivation. So as a beginner you have to decide whether you’re going to get on board with that. I didn’t, so I don’t know for sure what I’m missing, and what price I’d have to pay for it, and this idea may be pretty off the wall for this reason, but … if someone liked the idea of an Ocaml with better syntax and ppx features out of the box, it might not be that impossible, and I expect usable with existing libraries. (When I say “out of the box”, I mean a compiler - not a suite of things that includes the compiler but also depends on things like dune that have wide but limited applicability.)

1 Like

I gave up several times during last few weeks and sometimes I wonder why I still keep trying.
To me the biggest frustration points are:

  1. the culture of not having simple examples for the beginners
  2. dune, dune, dune, dune and many times more dune
  3. confusion about setting up the environment
  4. Real World Jane Street version of the language
  5. not understanding how to work with types coming from other packages, or compatibility of types in the context of ctypes
5 Likes

I understand your points, and it is great that you did not give up. However, as a small feedback:

  • I think that there are more and more examples for beginners, I find more difficult to find resources of the intermediate level. There is a lot of work now in the ocaml.org webpage for beginners. Yes, several sections of the “official manual” are not for beginners, but this more technical documentation is also needed.
  • (About dune) My general advice is try to understand first the: ocaml, ocamlopt, ocamlfind etc before going to dune… (actually it is explained before dune)
  • At least in Linux, setting the environment is fine. Happy to discuss about specific problems.
  • There are resources with examples of Real World Ocaml book in “plain” version of the language (in github) and a lot of examples and exercises derived from them (also in github). Anyway, I understand that several “standard” libraries can be confusion for beginners. I decided to learn first using only the “official” standard library that is working fine in most of the cases for a beginner.
  • Your point 5 is an intermediate level issue but happy to discuss about specific cases. To do a proper diagnostic of the problem with types coming from other packages it is good to give examples so we can understand (and improve) what you mean. The ctypes issue (I had/have similar issues) my advice is to try to ask here in OCaml Discuss specific questions with code snippets so others can solve concrete difficulties (for example this).
2 Likes

I got over my frustrations from 1-4 and I am stuck at 5:

I have no clue to the extent to which my trial-and-error results are incorrect.

There is no way to get over it without screen-sharing and live conversation.

Regarding examples for beginners, I did excercism puzzles and I do not mean that what you suggest.

I mean the lack of simple examples where you create a project with dune, and use a library.
It should not take you a week to figure out how to do it.

The ecosystem is crazily over-engineered.

Thank you for your clarification. With the information you gave, it was not possible for me to understand that it was mainly about dune rather than ocaml itself. For the example you gave us (ocaml-cairo plus ctypes) it might be better to open a specific topic/question for it.

In my experience the better you explain the problem the easier it is for others to help you. In the issue you have opened in github the other person needs to find out several things before they can give you an answer. For example, you mentioned cairo and ocaml-cairo but nothing about what you are trying to do which seems to be related to libgtk but you didn’t explain it explicitly.

Apologies, I just realized now that you already opened a topic/question about it here and also in reddit. Thanks.