Purity is almost used in a pejorative sense throughout the page.
If it wasn't for poor library support, I would prefer using SML to OCaml. SML is syntactically simpler and both SML/NJ and MLton have a lot to offer that I haven't yet found with OCaml.
I think one of the biggest mistakes in the life cycle of SML was premature specification. As a result of the language definition, SML has stagnated for almost 20 years. There is now some notion of "Successor ML" (see: http://sml-family.org/), but I honestly don't see it catching on outside of an academic space.
As an academic language, SML is great. It is a great tool for learning functional programming since the learning curve isn't especially steep. It is easy to reason about performance since evaluation order is explicit. And immutability is the default but with mutable types that are easy to use.
> I think one of the biggest mistakes in the life cycle of SML was premature specification.
I disagree. There were some earlier Successor ML efforts ~10 years ago (see: http://successor-ml.org/index.php?title=Main_Page ), but as I understand it, there was agreement on many of the small issues but larger challenges reaching consensus on bigger changes. You can see some notes from Bob Harper at the ML Workshop in 2013 on how we're moving forward currently:
We did the first part and very recently put http://www.sml-family.org/ online and announced the opening of the definition (whew!).
I'm primarily concerned with the parallelism and concurrency-related features, which we're trying to first clean up a bit more in the context of Manticore before trying to do something like proposing standardization for those features.
Also (IMO) you should take those slides more as a research direction for years of research investigating alternatives that eventually get standardized, not a 2015 implementation plan :-) Each of those topics casually mentions non-trivial extensions to problems that already have each had researcher-decades of investigation.
Given the goal of implementing proof systems, whose semantics you obviously need to be really clear, I don't think that specification was premature. But you're right that it has inhibited the use of the language in a more general setting.
I'm still waiting for OCaml to get decent multicore support, decent Windows support and an FFI that doesn't suck donkey brains through a straw (which is why so many OCaml libraries, like OpenGL bindings, suck beyond belief). Multicore became ubiquitous 10 years ago. Last I looked the vestigial OCaml community still hadn't noticed.
The netmulticore library [1,2] worked well for me. The style there is to spawn worker processes and pass messages between them. I'm not sure how popular that library got but that style of programming seems to be pretty popular among OCaml users who need it. Many projects use something lighter-weight or just roll their own (e.g. Parallel in Jane Street's ocaml-core looks pretty good [3]).
I certainly wouldn't mind if you could swap out the garbage collector (a la Java - where you can choose between a fast serial garbage collector and a parallel garbage collector). However, I don't think it's the right tool for many tasks.
TBH I don't miss multithreading in OCaml, if you write applications which expose some sort of API over a socket that will scale to clusters, not just multicore. And you can program those kinds of applications quite nicely with Lwt or Async already.
Regarding FFI, ocaml-ctypes is interesting, it allows you to bind to a C library at runtime using pure OCaml (and a wrapper around libffi), or to generate C stubs.
Regarding OpenGL there is a new binding called 'tgls' for OpenGL 3.x/4.x (besides LablGL) that is mostly generated from the XML description of the OpenGL APIs.
Regarding Windows support I can't say much because I don't use it at all, but there was a new 'Self-containted OCaml distribution for Windows' called ocpwin-distrib posted to the ML recently. If you care about Windows support you should probably give them feedback on what is missing.
I just watched it. Great to see somebody else trying but this is no more advanced than OC4MC and they are using Fibonacci as an example when I already used a complete parallel ray tracer with shared memory when benchmarking parallelised HLVM code:
They've got a lot of catching up to do before they can overtake and I'm concerned that retrofitting a modern GC on a 20th century VM will never work because OCaml has such poor locality of reference due to massive unnecessary boxing.
> TBH I don't miss multithreading in OCaml,
I said multicore, not multithreading. I dot lots of concurrency and parallelism in F# without having to worry about multithreading.
> if you write applications which expose some sort of API over a socket that will scale to clusters not just multicore.
You're proposing deep copying all of your data structures when multicore programming is all about sharing data between cores using shared caches. If you copy then your identical copies of the data compete for space in a shared cache.
> And you can program those kinds of applications quite nicely with Lwt or Async already.
Lwt and Async are for concurrent programming. In particular, they are extremely slow.
> Regarding FFI, ocaml-ctypes is interesting, it allows you to bind to a C library at runtime using pure OCaml (and a wrapper around libffi), or to generate C stubs.
Ctypes looks great. Didn't exist when I last used OCaml.
> Regarding OpenGL there is a new binding called 'tgls' for OpenGL 3.x/4.x (besides LablGL) that is mostly generated from the XML description of the OpenGL APIs.
>if you write applications which expose some sort of API over a socket that will scale to clusters, not just multicore
And it will be way slower for all those use cases where I don't need to scale to clusters. Or where it won't actually scale to clusters because I need shared memory, the latency to ask another node is simply too high. I had to move to haskell specifically because of ocaml's "who cares about multicore" stance.
I really liked SML and wanted to use it in anger after reading "ML for the working programmer" and "Purely functional data structures" (excellent functional programming books by the way) but just couldn't due to the lack of libraries as you say. For me the object system on Ocaml was just too much. Tis a pity.
> For me the object system on Ocaml was just too much
What do you mean by that ? The current consensus about the object system in OCaml is "don't use it", even if there might be cases where it will describe your system better.
The standard library (both the real stdlib and Jane Street's Core library) don't use objects so you can pretty much ignore this part of the language if you want to.
There's nothing wrong with objects or classes in OCaml -- it's just that most problems can be expressed using a simplest abstraction technique such as modules. OCaml has steadily improved in every recent release to further reduce the set of problems that need objects (open types, GADTs, first class modules, destructive substitution in module types).
For problems where objects and classes are a good fit (e.g. something requiring open recursion), then OCaml objects are fine and sound. They are used quite nicely in CIL for implementing the visitor pattern, and I like them in the Lablgtk OCaml bindings as well. They also provide a nice bridge to foreign code in OCamlJava and js_of_ocaml when compiling to platforms that make more use of objects.
It's worth noting that class types and object types are distinct in OCaml, so you can often use objects without involving classes at all. This is explained in Real World OCaml:
https://realworldocaml.org/v1/en/html/objects.html
Not using OCaml because it possesses an optional object and class extension is a little strange. It never really gets in the way...
Since that is the 'current consensus' as you say, it seems like the ocaml community converged on the opinion I formed 10 years ago. But it wasn't necessarily the consensus then.
I would note that several SML extensions were made by adding on to existing compilers (e.g. Concurrent ML). The Haskell community has used this model to great effect (testing/refining new features as add-ons to existing compilers and then moving some of them into the main language).
Any ML would be good. The basic features of the language are the best. "Vanilla" ML would suit most people's needs (preferable replacement to many popular languages).
ML's syntax is indeed less confusing than OCaml's. And that's the reason I'm working on a compiler front-end replacement for OCaml inspired by Clojure, Haskell, Ruby and Julia. It will be for OCaml what Elixir is for Erlang. I'm finishing the parser right now and will next implement the language primitives as a library in the language itself.
Do you share the development anywhere online? Are you using Menhir? I could maybe help you if you're having any troubles with the parser, I've been working on many interesting and hard (LA)LR parser issues in the past few months. What will the syntax be like, any existing language, maybe like Julia?
I don't have the code publicly available yet, but all the development will happen openly on GitHub. I estimate to prepare a minimal working compiler in the next weeks.
I've been thinking a lot about how to perform the parsing. I believe it's important to have a complete metaprogramming system (with code macros and quasiquotations) with all the basic language primitives defined in the language itself. So standard parsing with Menhir may not be ideal for this purpose. Instead, I've ported a simple top-down operator parsers (also known as Pratt parser) to OCaml, as described for example here[1]. This is the technique used by Douglas Crockford for the JSLint parser[2].
The core of the language can be seen as a simple compiler toolkit, that parses generic expressions and produces native OCaml AST. So for example even things like assignment (`=`) or function definitions are regular macros. This is one of the reasons I named the language "Meta".
There's another interessting language that inspired me a lot called Magpie[3] which uses identical approach (described in detail here [4]).
The main goal of the language is simplicity. OCaml is a very powerful language but programming in it requires some writing effort. I want the freedom and natural expressiveness of Clojure or Python combined with type security guarantees and modularity of OCaml.
I'm developing a large system for my startup in OCaml right now and plan to incrementally port it to Meta, which I think is important for dogfooding experiments, for support and commitment.
The syntax will look a lot like Julia (although I considered to just adopt s-expressions, languages like Elixir and Julia showed as that it's possible to be homoiconic (in some restricted sense) and still use regular syntax). I am still in research and only have defined basic language constructs like variable bindings, function definitions, type annotations, pattern matching and macros. If you are interested I can show you some examples.
What do you think about it? It would be nice to hear some early feedback.
Please don't name it just "Meta", it will make it a lot more difficult to find resources for it in the web. Imagine googling for "meta", "meta lang", "meta language", "metalang". Maybe something to go with "meta", like MetaCaml.
Agreed, just meta probably isn't a very good name (neither is standard meta-language for that matter, but at least the abbreviation sml isn't entirely hopeless). Just "meta" would be rather close to OMeta[1] as well.
I tried to see if there might be a more generic animal name to use for inspiration, and so learned that: "The even-toed ungulates (Artiodactyla) are ungulates (hoofed animals) whose weight is borne approximately equally by the third and fourth toes, rather than mostly or entirely by the third as in odd-toed ungulates (perissodactyls), such as horses.". Which while somewhat interesting doesn't really reveal an immediately usable name. But at least Artiodac should be unique...
I thought it was rather obvious in context that camels are even toed ungulates, and so the group to which camels belong (but a more generic term); and the language was a a kind of meta-(o)-caml, so looking for something that was related to camels, but more generic might be fruitful in order to find a name that was both unique, and also had a flavour of being "meta-caml" (or meta camel). Using myself as a the universal yardstick, I can say with certainty that not all people familiar with (o)caml and camels are aware camels are ungulates, let alone even toed ungulates... hence that particular avenue was a bit of a dead end...
Anyway, the Hyrax looks almost as interesting as the honey badger -- certainly a worthy code name -- but perhaps not for meta(o)caml IMNHO.
[edit: re-reading the language authors comment that: "It will be for OCaml what Elixir is for Erlang." -- I suppose something that is to a Camels as Elixir is ... to an abstract concept? Options might include: Water, Bedouin, Desert?
I also note that: "The earliest known camel, called Protylopus, lived in North America 40 to 50 million years ago (during the Eocene). It was about the size of a rabbit and lived in the open woodlands of what is now South Dakota."
I don't think protylopus is a very good name either, but probably better than ungulate. Perhaps "Dakota Caml" or Dakota Meta Language (dml) might work :-)
]
So, I think my ideas about syntax are similar in their goals, but different in some details.
1) I experimented with Pratt parsers, and actually made a great, extensible, full-blown parser, but the devil was in the details - if you parse your whole language with a Pratt parser, you have to get the operator/keyword precedence and associativity just right. It's probably possible to get it just right, but the problem with Pratt parsers is that you just don't know it; in particular, you don't know if your syntax has any ambiguities (i.e. things that might not parse the way you want them to).
2) Because of that, I abandoned Pratt parsers and went back to LALR(1) (yacc). It's tedious and complicated, but it has the nice property that it notifies you of all syntax ambiguities, a property that I haven't found in any other parsing system (recursive descent/LL, Pratt, PEG). Of course, some people say that PEG is unambiguous, but these people are either stupid or ignorant; PEG just doesn't tell you where the ambiguities exist, and always takes the first choice. LALR is "not ambiguous" in the same way, in case of shift/reduce conflict it always chooses shift, in case of reduce/reduce conflict it chooses the first choice, but at least it tells you where the choices were made, so that you can examine and fix them.
3) LALR(1) is also quite stupid and limited, which is why Menhir has been a blessing - it's practically as efficient as ocamlyacc (in theory, at least - ocamlyacc produces compiled C code, while Menhir produces OCaml with Obj.magic), but parses LR(1) instead of LALR(1), which makes writing grammars for it much easier, and produces nicer error messages. I've managed to write a very flexible, Julia-like syntax (except with {} instead of begin/end) that supports tuples, arrow function syntax, and pattern matching.
4) Extensibility is important for me, but I intentionally want to limit it - I don't want programmers to be able to (re)define basic language syntax, like ` = `, as that could fragment the code and make the syntax unpredictable/ambiguous. However, I want to include user-defined operators (with custom precedence/associativity), which could be done using an embedded Pratt parser, and Julia-like macros that are always preceded by `@` and can only be used in a few predetermined forms (function calls/statements/blocks). I think that makes the syntax much more manageable and readable. Also, I don't like Elixir's syntax, to many semicolons/`do:` keywords. I haven't actually implemented the above yet, but I think it could be done within my current Menhir parser infrastructure.
Another thing: I too strongly discourage the name Meta, especially for OCaml, because there is already a project that's called MetaOCaml.
I'm curious about what you find particularly confusing about OCaml. I'm a (relative) newcomer to the language, but I find it extremely readable, even if it's a bit clunky at times.
Good OCaml code is one of most readable in the category of static languages, I agree. But it's less "writable" than readable in my opinion. Of course you can get used to the syntax, but for me expressiveness and clarity of comprehension comes from both: simplicity of writing and reading.
Often I feel like there is a barrier between your thoughts and their execution which does not exist in Python or Ruby for example. When I switch from OCaml to Python it's like a breath of fresh air.
In conclusion I think common tasks must be syntactically abstractable. For example, I find working with standard data structures in OCaml annoying, as if I were programming in C.
There are tons of syntax extensions for OCaml that try to fix these defects, but it only supports my claims.
Learning Python was certainly easier for me. I could really hit the ground running. OCaml had a much steeper learning curve. It took me a while to get used to the compiler and the different style of programming.
However, I'm a professional. Some of the powerful tools I use take a serious investment to learn. Here it paid off well.
Now I find that Python programs are easy to start but OCaml programs are easy to finish. The compiler is an invaluable helper that I miss when using other languages. Some syntax is clunkier than others but to me having good types (and a good compiler) makes or breaks a language. I can get my work done in anything but this is an area where the language really helps me out.
if a > 0 then
print_endline "a > 0" ;
match a with
| 0 -> print_endline "impossible"
| 2 ->
match b with
| 0 -> print_endline "2, 0"
| _ -> print_endline "2, not 0"
| 3 -> print_endline "3, something"
| _ -> print_endline "something, something"
;
print_endline "done"
Any decent programming editor will be able to indent that properly and you will see the problem. Also, I agree that it is a bit ugly but it's not that complicated to understand how the match syntax works. Adding a "begin" and an "end" in this code is simple enough :).
It's not just the match syntax; it's also the `;` expression separator.
> Any decent programming editor will be able to indent that properly and you will see the problem.
This seems like a weak excuse. In particular, I could turn it around and say, "any decent programming language should be writable without an editor". Also, the issue isn't just reading, it's writing too - it's much harder to foresee/plan all the `begin`/`end`, while you're writing a line of code, that will make the lines that follow work as intended.
Fair enough, but for the ';', as others explained meanwhile, I think it's pretty simple to understand, it's just that we are not used to it because of C syntax.
EDIT: actually, you are right about ';', it is confusing when I think about it: it does not have the same behavior in a branch of a `match` and in the branch of an `if`… I wonder how I never had problem with that before.
OCaml will give you compiler warnings on the incomplete and redundant match cases. F# will silently execute a semantically different version of your code. I prefer OCaml. Indentation sensitivity was one of F#'s design flaws.
1) Ignore indentation (I intentionally wrote it to deceive, but similar examples could easily appear in real code). 2) `match` cases always bind to the innermost `match` statement. 3) Statement separators (`;`) are tricky.
The correct indentation of the above code is:
if a > 0 then
print_endline "a > 0" ;
match a with
| 0 -> print_endline "impossible"
| 2 ->
match b with
| 0 -> print_endline "2, 0"
| _ -> print_endline "2, not 0"
| 3 -> print_endline "3, something"
| _ -> print_endline "something, something"
;
print_endline "done"
To have the same meaning as the indentation implies, you would need to add `(...)` or `begin ... end` at the appropriate places.
What would be the corresponding SML? I see how the way SML binds ';' would help here, but would it change anything about the last two badly indented matching? And it seems to provide you with the equivalent footgun if you attempt to do multiple side-effect operations in a match branch, which in my experience you want to do more often than you need to separate matches with ';'.
Sorry I realized my mistake and deleted my comment before I saw yours.
FTR, I was saying that the ";" in the `then` clause of the if statement didn't end it, but it actually does. I see that arjunnarayan got this wrong too in his comment here: https://news.ycombinator.com/item?id=8498187.
There are two things to remember. The first is the match binding thing that tomp mentions in his reply.
The second one is that in OCaml, semicolon is a separator, and not a terminator. In contrast, in C/C++, semicolon is a terminator. If you have an expression, you end it with a ";" just because.
This is not the case for OCaml. In OCaml, semicolon is used to separate two sequential expressions where only one expression is expected. Thus,
<expr1>;<expr2>
is evaluated in sequence, and can be used in a place where only one is expected. For example, if statements have the following syntax:
if <expr1> then <expr2> else <expr3>
Now if you wanted to do two things (instead of one) in the "then" block, you would simply write
if <expr1> then <expr2.1>;<expr2.2> else <expr3>
Notice that under these rules
if <expr1> then <expr2>; else <expr3>
makes no sense. Separators are not terminators. We are used to thinking of ";" as terminators because of C/C++.
Actually, this will not work, I was also confused by it.
# if true then (); 1 else 2;;
Error: Parse error: [str_item] or ";;" expected (in [top_phrase])
# if true then begin (); 1 end else 2;;
- : int = 1
# if true then ((); 1) else 2;;
- : int = 1
The confusion comes from the fact that it doesn't behave the same way in match expressions:
# match true with | true -> (); 1 | false -> 2;;
- : int = 1
I'll add that you need ';'-as-separator (you also use ';' in other places, such as when separating items in a list) only when calling functions which return 'unit', which is typically functions which perform side-effects (eg, close a file). That's usually a small portion of your program.
Out of curiosity, why do you think Opam is awesome? I recently had to install a few OCaml projects and I had a hard time dealing with opam and ocamlfind (both on mac os and a linux VM). I suppose it's fine once everything is properly set up.
I have the impression that the OCaml world has seen a lot of changes recently with a lot of complexity added.
I think Opam is very cool because it simplifies OCaml development a lot, just like any language package manager does for its language.
With Opam I can deploy my code on different machines very easily by installing the right version of OCaml and library dependencies in just a few command, and it just works.
The problem with ocamlfind you are mentioning was just a temporary bug I think, I also encountered it, but it will be fixed real soon, see: <https://github.com/ocaml/opam/issues/1671> for instance.
> I have the impression that the OCaml world has seen a lot of changes recently with a lot of complexity added.
The first part of that is very true but the second part feels completely and utterly false to me. Where is this added complexity?
OCaml and OPAM were ridiculously trivial to set up on my system (via homebrew on a Mac) and OPAM 1.2, with many improvements, is due for release in the next few days [1]. Decent instructions are in the RWO wiki page [2]. The OPAM devs are responsive to bug reports and do a lot to maintain the health of the package ecosystem too.
If you're installing everything from sources then you're choosing to take on that burden and I wish you well but don't claim that more complexity is being added.
> The first part of that is very true but the second part feels completely and utterly false to me. Where is this added complexity?
Recently, I tried to install a few ocaml projects. I had to learn what are Opam, OCamlfind, Batteries, Core... And unfortunately, it didn't work on my particular mac with homebrew. I had to use alternative ways of installing everything.
If you look at the installation procedure:
https://ocaml.org/docs/install.html
It may be rather complex depending on the platform, and if it doesn't work from the start, you have to dig deeply in this whole new ecosystem.
An other example. I was trying to install a project that compiles only using ocamlfind. Unfortunately, one of the libraries used wasn't available with Opam and I had to find it elsewhere and write myself the ocamlfind meta information.
So yes, to me, a lot of complexity was added. There are many new tools that you have to learn, a few standard libraries (I still don't know which one to use as a casual developer).
It's probably much powerful and it works well most of the time, but it's more complex than 10 years ago.
Nope, none of what you describe equates to 'complexity', there just happens to be more 'stuff' (unless you consider other, more popular languages with even more tools/libraries to be even more complex, in which case we have different viewpoints on what that word means in this context).
Sure, not everything is in opam and that can be quite frustrating but the improvements over the last few years have done a lot to reduce the pain that people experience (not increase it). Seriously, would you rather have the old ecosystem back? I know a large number of devs who would be quite hostile to that idea.
The standard library question isn't new and has been going on for some time but the options now are substantially better then they ever have been. Ask around and get feedback from others on what might suit your needs best. Until the community naturally converges on something, this is just how it is.
> "And unfortunately, it didn't work on my particular mac with homebrew."
So what went wrong? The instructions for homebrew are two lines and my experience here was flawless. The slew of instructions you point at are largely for Linux distros and most of those are also only a few lines. Were you trying any of these? It's clear that the install page you point to needs work (who still uses fink?) but in most cases it's just a time-consuming process, rather than a difficult one (i.e you just sit there while things compile). If something goes wrong, then reporting it on the list [1] or issue tracker [2] will help others to fix it.
I find 'ocaml switch' very useful (sandbox functionality). Also, you can add a repo for your local stuff very easily. The CLI is pretty nice and well-designed.
It just can't be activated by default since GitHub doesn't support multiple commit updates yet, so we have to chose between Travis and Appveyor. When GitHub sorts that out, we can have Linux, OSX and Windows tests for all new packages. In general, help porting packages to Windows is very welcome from interested parties.
One thing I think F# got right is that it removed some of the "flexibility" in OCaml's syntax. For example, it uses significant whitespace to define scoping, which removed (in nearly all cases) the need for the 'in' keyword and lots of parentheses and semicolons. That may not sound like a huge deal, but it does greatly improve the readability of F# code (all other things being equal).
> One thing I think F# got right is that it removed some of the "flexibility" in OCaml's syntax. For example, it uses significant whitespace to define scoping, which removed (in nearly all cases) the need for the 'in' keyword and lots of parentheses and semicolons. That may not sound like a huge deal, but it does greatly improve the readability of F# code (all other things being equal).
I must disagree. I once translated a significant (15kLOC) commercial OCaml code base into F# and took the opportunity to quantify the syntactic benefits as <3% by volume of code. The flipside is that F# misinterprets code pasted from the web, rendering most code on the web useless. That is a crippling deficiency of F# in my eyes and a major reason to go back to the superior OCaml solution.
Really? I can see how it's nicer in some places, but you'll take currying and or patterns out of my cold, dead hands (and there are some cases where you do need objects).
I really enjoy working with SML (I've used sml/nj and parrotml) - and would love to use it for "real" projects, but until it actually has decent unicode support, there's just no way I can.
If it wasn't for poor library support, I would prefer using SML to OCaml. SML is syntactically simpler and both SML/NJ and MLton have a lot to offer that I haven't yet found with OCaml.
I think one of the biggest mistakes in the life cycle of SML was premature specification. As a result of the language definition, SML has stagnated for almost 20 years. There is now some notion of "Successor ML" (see: http://sml-family.org/), but I honestly don't see it catching on outside of an academic space.
As an academic language, SML is great. It is a great tool for learning functional programming since the learning curve isn't especially steep. It is easy to reason about performance since evaluation order is explicit. And immutability is the default but with mutable types that are easy to use.