Comparison to scheme

Does scheme/racket has essential features which are missing in ocaml?
Or which can be re-implemented in ocaml ?

I’d argue they’re very different languages when it comes to the feature set. As in comparing them would be apples-to-oranges kind of situation.
If so, a more productive question may be what features from Racket (or other schemes) one would like to have in OCaml… Not just whether features are missing.

I haven’t used Racket extensively enough to be qualified to answer this…
Instead I’ll share a “feature” that’s not part of the language, but from my little exposures to their ecosystem: I’d love to have a docs site as good as theirs.

5 Likes

Is this a purely academic question or used to decide which language to use? As a starting point, two differences I would name:

  • Difficult to emulate in Scheme: static typing and the module system
  • Difficult to emulate in OCaml: Scheme macros
2 Likes

The question was posed in large context, but i wanted to digest which language is best fit for which kind of problems.

One word: macros.

Or to be more precise, an easy to use, easy to check macro system. The current sota w.r.t meta programming in OCaml is through ppx extensions, but these require directly manipulating OCaml’s AST data structure, which can lead to macros that are hard to understand and debug.

2 Likes

Scheme/Lisp is great if you need to embed an interpreter so that the behavior of your program can be modified at runtime. A good example is the Emacs editor which exposes its programmability through its own version of Lisp. As someone famous once said to the tune that any program with complex enough behavior probably has an embedded Lisp interpreter running somewhere, potentially buggy if the programmer hasn’t realized that is the case!

OCaml’s power is mostly in statically type checking your program so that your program is more likely to run correctly. You can write large programs with more confidence. Your program is also easier to maintain as the types don’t lie but the API documents or comments (if they exist at all) could. The code generated is quite fast and the compiler is really fast. You get more help from your LSP while writing your code than almost anything else.

1 Like

Incidentally does anyone know if there is any way to embed Lisp/Scheme in OCaml? Standard embedding of an external implementation seems challenging due to dueling garbage collectors. Probably best achieved through IPC in which case one should probably just go with the modern Lisp (I mean Javascript :smile: ). Does anyone do IPC between utop and native compiled code? Data marshaling would be much easier with utop, no?

1 Like

Not exactly what you are after but you can embed Lua via lua-ml. The accompanying paper shows a few technique on how to embed a language in OCaml.

1 Like

Note that there are pure OCaml implementations such as GitHub - freehck/ocs: OCS: a mirror of the ocaml OCS scheme interpreter .

Cheers,
Nicolas

Keep in mind that this is an implementation of Lua 2.5, not quite the same as the 5.1/5.2/LuaJIT/5.3 language that people use these days. But for scripting it might be sufficient.

The main important selling point I would say is hygienic macros, as mentioned by @Gopiandcode. While the situation has improved much from Camlp4, Camlp5, omp and now ppxlib, the language itself doesn’t really have support for defining new syntax.

The issue at hand is two-fold: Scheme’s syntax is essentially just (valid) S-expression that have an attached semantic to them. So you can define your own interpretations of these s-expressions, to expand into simpler s-expressions that eventually expand in special-forms. OCaml doesn’t have a structure like this, the AST is not accessible to the regular user and it is typed so you can only represent what the authors of OCaml have forseen.

Then the other thing is that the macro-system is integrated in the language, a macro is essentially a function of an s-expression to and s-expression (syntax-rules even implements a system that is reminiscent of pattern patching). Similarly like functors can be thought of as functions from modules to modules, integrated into the language. Whereas OCaml PPX macros work by running the OCaml program through a program which outputs another program. This works similarly, but it is not as integrated (in a way, how people implement Go generics by using external code generators).

The system is a bit more involved than that because OCaml has a few useful additions for PPXes but I would say a macro system like Scheme just can’t be implemented in OCaml, for better or worse.

1 Like

I keep wondering about this. Theoretically, shouldn’t it be possible for the compiler to provide a quote/unquote macro mechanism hides the raw AST transformations? Rust, Crystal, and other typed languages seem to do that.

This sort of conversation, that might be over-simplified to “can we have schemesque macros”, always makes me think “Doesn’t MetaOCaml already do better?”. So, genuine question: am I confused, or is there something missing, or…?

Perhaps I’m missing something. Is anyone using MetaOCaml in production in industry? It seems like the compiler team did not wish to pursue that direction and chose to go with PPX instead?

MetaOCaml serves completely different purposes. Unlike general macro-processors and syntax-rewriters, which are mostly used for reducing the boilerplate in cases when the other language constructs are not expressive enough, MetaOCaml’s purpose is runtime code optimization. One of the most important distinctions is that the MetaOCaml interface is purely generative, i.e., it only lets you generate the code values, but never inspect them. Therefore, we can’t really say that MetaOCaml is a macro-processor.

As noted above, those features solve completely different tasks. What could be solved with PPX couldn’t be solved with MetaOCaml and vice verse. MetaOCaml is about multi-staged code optimizations. And while it is possible to do some of them using staged compuation features of Lisps, it is totally possible to implement them without any macro-processing. For example, Refal has a supercompiler and does staged computations without any macro-processing. It even lacks the macro system, despite that it is monoiconic and is basically a variant of Lisp with trees instead of lists.

5 Likes