Programming languages enthusiast. Author of Learn Type Driven Development: https://www.packtpub.com/application-development/learn-type-driven-development
Just fyi, a few points in this post which need to be corrected.
ugliest syntax of any major programming language. Double semicolons are only the start
Double semicolons are actually not needed or used in OCaml source code for a long time now. Your 'multiple statements' example would be written idiomatically like this:
letask_for_name()=print_string"What's your name? ";read_line()let()=Printf.printf"Hello, %s!\n"(ask_for_name())
There's no string interpolation
However, as you mention later in your post, and as shown above, there is printf-style formatting, so the Fibonacci example would look like:
It has some (atrocious) macro functionality, which allows Printf.printf (with static template only)
Printf-style formatting is not using macros, it is all defined in the standard library. And, using static templates is actually a security best practice (you may have heard of things like the log4j vulnerability that may happen if you allow format strings to be injected at runtime).
Oh and you might have noticed that OCaml has no idea what Unicode even is
Yes, Unicode is not handled in the language or in the standard library. The string data type does not know or care about the encoding of the string's contents. But there are good third-party libraries that handle it. Unicode is actually very tricky to handle properly and the small OCaml team made the (for them) correct decision to not try to do it themselves. See this thread for more details: reddit.com/r/programming/comments/...
By the way these names generally need to be unique everywhere, so you can't really reuse it in some different interface which would take I int | F float
The simple way to reuse the same constructor names is to put them in different modules. Constructors are unique when namespaced by their modules. So e.g. you can have:
Oh and OCaml does not love if you reuse field names between different types. So type point3 = {x: float; y: float; z: float};; isn't forbidden, but it causes issues and would require a lot of manual type annotations.
It's actually not that bad if you follow the normal practice of putting the records in different modules, e.g.
Now it's easy to distinguish between them by just providing the module prefix for at least one field e.g. let p2 = { Point2.x = 1.; y = 1. } or let { Point3.x; _ } = p3.
Should you use OCaml?...No. And I'm saying it as someone who's done a lot of OCaml back in the days.
Interesting. I guess you must have used it a long time ago and in a fairly restricted way. A lot of what I mentioned above is standard practice now.
I will say I agree that just being able to polymorphically print out any data is definitely an OCaml pain point. But on the whole I can't agree with your conclusion :-)
Nope, printf is done with special macro system OCaml has just for format strings, you can see the compiler hooks here: github.com/ocaml/ocaml/blob/trunk/...
You cannot actually write printf-like function yourself, or customize the ones ocaml has, without either modifying the compiler, or using an external macro system like camlp4 (or I guess ppx now, I haven't touched that in years).
As for putting names in modules, this doesn't actually solve this issue, as when you want to pass S some_str to a polymorphic function, you'd need to know exact type of polymorphism it supports, like foo (SIF.S s) vs bar (SI.S s).
This isn't required by any theoretical reason, Haskell deals with this perfectly fine, and even SML had some limited type classes IIRC.
I'm not sure there's one standard OCaml style like with Python. I double checked it with OCaml source code, and it's full of ;;s. And tbh I'm not sure starting a lot of subsequent lines with let () = as it also sometimes does is really an improvement. I'll leave it to the readers to decide which one is more readable (first one from some tests in OCaml source):
let () = show (not_greater_equal 1.0 2.0)
let () = show (not_greater_equal 1.0 1.0)
let () = show (not_greater_equal 2.0 1.0)
let () = show (not_greater_equal 1.0 nan)
let () = print_line ()
vs
show (not_greater_equal 1.0 2.0);;
show (not_greater_equal 1.0 1.0);;
show (not_greater_equal 2.0 1.0);;
show (not_greater_equal 1.0 nan);;
print_line ();;
Programming languages enthusiast. Author of Learn Type Driven Development: https://www.packtpub.com/application-development/learn-type-driven-development
Nope, printf is done with special macro system OCaml has just for format strings
Format strings are part of the equation, yes, but as the comment you pointed to notes, the printf functionality is defined in normal functions in the Printf/Scanf/Format modules. Format strings are just a syntax sugar; it's possible to desugar them to (something like) printf [Text "Hello, "; Format_s; Text "!\n"] name which would do the same job, just a little more verbosely.
This isn't required by any theoretical reason, Haskell deals with this perfectly fine, and even SML had some limited type classes IIRC.
Well, Haskell and OCaml explicitly are in very different design spaces here, and both have valid reasons for doing what they do. OCaml's more explicit style makes compilation faster and error messages simpler, for example. And SML doesn't have type classes, unless you're talking about the built-in non-extensible equality types.
I double checked it with OCaml source code, and it's full of ;;s
Yes, very old code still uses ;; but that has not been the convention for a long time now. For example, if you check the link you gave above, very little of that file is using ;;. And if you use ocamlformat, the standard code formatter, it will idiomatically remove ;;.
And tbh I'm not sure starting a lot of subsequent lines with let () = as it also sometimes does is really an improvement
Yes, we actually don't need to do that either. We can use ; which is the sequencing operator to do it pretty cleanly:
Just fyi, a few points in this post which need to be corrected.
Double semicolons are actually not needed or used in OCaml source code for a long time now. Your 'multiple statements' example would be written idiomatically like this:
However, as you mention later in your post, and as shown above, there is printf-style formatting, so the Fibonacci example would look like:
Printf-style formatting is not using macros, it is all defined in the standard library. And, using static templates is actually a security best practice (you may have heard of things like the log4j vulnerability that may happen if you allow format strings to be injected at runtime).
Yes, Unicode is not handled in the language or in the standard library. The
string
data type does not know or care about the encoding of the string's contents. But there are good third-party libraries that handle it. Unicode is actually very tricky to handle properly and the small OCaml team made the (for them) correct decision to not try to do it themselves. See this thread for more details: reddit.com/r/programming/comments/...The simple way to reuse the same constructor names is to put them in different modules. Constructors are unique when namespaced by their modules. So e.g. you can have:
I'd be interested to know what those problems are.
Exactly, well, except it's not a macro :-)
Note that you can redefine standard operators like
( + )
for new data types, and many modules do that e.g. github.com/ocaml/Zarith/blob/48524...It's actually not that bad if you follow the normal practice of putting the records in different modules, e.g.
Now it's easy to distinguish between them by just providing the module prefix for at least one field e.g.
let p2 = { Point2.x = 1.; y = 1. }
orlet { Point3.x; _ } = p3
.Interesting. I guess you must have used it a long time ago and in a fairly restricted way. A lot of what I mentioned above is standard practice now.
I will say I agree that just being able to polymorphically print out any data is definitely an OCaml pain point. But on the whole I can't agree with your conclusion :-)
Nope, printf is done with special macro system OCaml has just for format strings, you can see the compiler hooks here: github.com/ocaml/ocaml/blob/trunk/...
You cannot actually write printf-like function yourself, or customize the ones ocaml has, without either modifying the compiler, or using an external macro system like camlp4 (or I guess ppx now, I haven't touched that in years).
As for putting names in modules, this doesn't actually solve this issue, as when you want to pass
S some_str
to a polymorphic function, you'd need to know exact type of polymorphism it supports, likefoo (SIF.S s)
vsbar (SI.S s)
.This isn't required by any theoretical reason, Haskell deals with this perfectly fine, and even SML had some limited type classes IIRC.
I'm not sure there's one standard OCaml style like with Python. I double checked it with OCaml source code, and it's full of
;;
s. And tbh I'm not sure starting a lot of subsequent lines withlet () =
as it also sometimes does is really an improvement. I'll leave it to the readers to decide which one is more readable (first one from some tests in OCaml source):vs
Format strings are part of the equation, yes, but as the comment you pointed to notes, the printf functionality is defined in normal functions in the
Printf
/Scanf
/Format
modules. Format strings are just a syntax sugar; it's possible to desugar them to (something like)printf [Text "Hello, "; Format_s; Text "!\n"] name
which would do the same job, just a little more verbosely.Well, Haskell and OCaml explicitly are in very different design spaces here, and both have valid reasons for doing what they do. OCaml's more explicit style makes compilation faster and error messages simpler, for example. And SML doesn't have type classes, unless you're talking about the built-in non-extensible equality types.
Yes, very old code still uses
;;
but that has not been the convention for a long time now. For example, if you check the link you gave above, very little of that file is using;;
. And if you useocamlformat
, the standard code formatter, it will idiomatically remove;;
.Yes, we actually don't need to do that either. We can use
;
which is the sequencing operator to do it pretty cleanly: