Now that Rust is around and supporting multicore, that's probably where I'll be investing my time.
I'd love to hear feedback from people who have used both Rust and OCaml.
Now that Rust is around and supporting multicore, that's probably where I'll be investing my time.
I'd love to hear feedback from people who have used both Rust and OCaml.
In short, OCaml is a mature language that has been used for decades in commercial applications. I feel OCaml is the next progression for the people that got excited about distributed systems via the Erlang path and want more of the safety and reasoning that comes from a strongly/statically-typed language like OCaml. Rust may or may not take off, but I am confident OCaml will remain viable for the foreseeable future, and probably gain slow, but steady popularity as engineers see all the cool things you can do like MirageOS: http://openmirage.org/
They belong to the same family, and they both share a common ancestor that is not object-oriented. But their object systems are very different from each other.
Well it's not inaccurate. Both of them were designed to make C object-oriented, and they tend to be used for many of the same situations because of that.
A more accurate nomenclature would be ".NET's OCaml-equivalent" or "Apple's C++-equivalent" (much like how C# is characterized as ".NET's Java-equivalent). This falls apart with thorough inspection, of course, but it's good enough for tongue-in-cheek comparisons like this.
To call it the .NET OCaml is not too far from the truth. And it was clearly meant tongue in cheek!
Last I checked, there was decent third-party library support in Batteries. I imagine it would be painful if you were to use Batteries' "UTF8.t" string type and had to interface with some other library that used "string" or some other string solution (like Camomile)?
If you'd like things encoded in some way, that's up to you, there is no type to help you (there is a Unicode module which can help convert between encodings)
Writing the appropriate glue isn't very hard, the interfaces either work with bytes or have to/from-bytes functions, but I suppose it's a bit annoying (at least when first starting out with the language) to have to figure out which lib is needed for which type of string operation, e.g. if you're into Batteries you still need Camomile (or uucp) for lowercasing:
module C = CamomileLibraryDefault.Camomile
module CM = C.CaseMap.Make(C.UTF8)
module U = Batteries.UTF8
let lower_initial bytes =
U.sub (U.of_string_unsafe bytes) 0 1
|> U.to_string_unsafe
|> CM.lowercase
let () =
lower_initial "Åge" |> print_endline (* prints "å" *)There are some areas where OCaml is more advanced than F# (functors, the codegen from the optimized compiler, lack of the msbuild barf sandwich, less hacky on non-Windows platforms), but there are also plenty of areas where F# is more advanced than OCaml (computation expressions, code interoperability, real 32 and 64 bit integers, agents, multicore runtime).
I would say that if OCaml was ideal for you except for the lack of parallelism, then you should definitely check out F# before you go all the way to Rust. Rust is awesome and for the right use case you should use it, but F# is a lot closer to OCaml than Rust is.
On the other hand, Rust (like Erlang) reinvents and wraps a lot of these calls in ways that are not immediately obvious. (Or at least, not AS immediately obvious as they are in OCaml.)
This is such a tremendous aid because there are nearly limitless documents and examples of the Unix API.
* network effect / hype
One of the main reason. It sounds lame, but I can justify to a customer re-writing a project in Rust because they've heard about it and they will be able to hire people who have either used it or at the very least will be interested in learning it. Also, they network/hype means that we are going to see good libraries emerge fairly quickly.
* multicore support
A year ago I was ready to move to OCaml, bought books, started to learn it, but the multicore situation was worse than python. Today we hear that "there is a good chance it's going to get multicore support". In Rust, it's already there, and it's not and after thought.
Rust also happened to be slightly faster for most things according to micro-benchmarks, but we know how reliable those are, and it's we're not talking order of magnitudes here, although it is early and we can hope it gets even better.
I ended up using a combination of python threads and processes, which was more work I wanted to do, and still relatively slow.
Also, it's trivial to write multithreaded extensions in C or C++ (at least for data processing). You just have to make sure to release the GIL.
IMO the trick is to just use the Python C API, and not use any wrappers like SWIG or ctypes. The Python C API is a little odd but it is fairly explicit. Also, it's better to write plain functions rather than classes, but for data processing that is natural anyway. If you need a class, do that part in Python.
Writing "one way" extension functions (that don't call back into Python) is quite easy and can give you a huge performance boost.
I think people get hung up on Python extensions because they are using TWO unfamiliar languages -- the wrapper language, and C/C++. But if you are just using one additional language, it's not hard to figure out.
I'll have to watch the video below, but don't you have to write some sort of Rust wrapper for every definition in Python.h? What about macros like Py_INCREF and Py_DECREF? I'd guess that someone has or will eventually do that work, but it's yet another layer of complexity, which can have the same downsides as SWIG.
For better or worse, the Python C API is tighlty coupled to the C implementation. That makes it very difficult to be more natural and understandable than C. It's a fairly huge API: https://docs.python.org/2/c-api/index.html
People always try to cover it up with "nice" abstractions, but they invariably end up being leaky (e.g. with respect to threads, garbage collection, OS portability, etc.)
Another huge can of worms: the build system. With a plain C extension, all I need is a C compiler on the system, and I can just do "python setup.py build". The situation with Windows is also quite messy -- I can't imagine Rust making it better.
I have experimented with creating normal .o files from OCaml and linking them with .o files from C/C++. That is a great feature of the OCaml toolchain. Still, I remember the documentation being sparse and I don't even remember how I did it at the moment.
FWIW, my main language is Python, but I love OCaml for certain things, and have grown to like C++ as well. Rust seems very interesting to me for its security properties and because it has native threads rather than including a mini-OS in the runtime like Go. My understanding is that Go is hopeless for many kinds of Python extensions, precisely because of the runtime issue, and calling from Go back into Python. (at least this was true a year or 2 ago)
I'm always wary of creating more layers than necessary. A system composed of Python and C will necessarily have fewer layers than one composed of Python and Rust, simply because Python is written in C and its interface is defined in C.
EDIT: I left out the BIGGEST point -- a dealbreaker. Python does NOT have an ABI. It has an API. AFAIK, that means you have to write a Rust ABI-compatible wrapper for EVERY VERSION of Python.
https://www.python.org/dev/peps/pep-0384/
To make a generalization, programmers learn about C APIs before they understand what the ABI is. It's just more concepts that you need to know about to write correct code. So if you just want to speed up an existing Python program, I would still recommend using a simple C or C++ extension. This solves both single-threaded speed issues and give you parallelism with multiple cores, so you will get huge speedups.
I think Rust can be easier to learn than C. You get high-level features like modules instead of include guards and preprocessor hacks, and you can skip learning the part about how to debug segfaults.
> I'll have to watch the video below, but don't you have to write some sort of Rust wrapper for every definition in Python.h? What about macros like Py_INCREF and Py_DECREF?
We have bindgen to automate most of that for you. For macros, you do need to duplicate them, but people should just do that once (and Google brings up several projects in which people have already written bindings for that).
> People always try to cover it up with "nice" abstractions, but they invariably end up being leaky (e.g. with respect to threads, garbage collection, OS portability, etc.)
But I don't see how Rust makes that any worse than in C.
> Another huge can of worms: the build system. With a plain C extension, all I need is a C compiler on the system, and I can just do "python setup.py build". The situation with Windows is also quite messy -- I can't imagine Rust making it better.
Yes, you do need to add Rust support to the extension's build system. That is fair, but I think Rust's advantages outweigh that cost :)
> A system composed of Python and C will necessarily have fewer layers than one composed of Python and Rust, simply because Python is written in C and its interface is defined in C.
That doesn't make any sense to me. It's all machine code at runtime. The pertinent question is whether Rust adds any measurable abstraction taxes at runtime (it doesn't) and whether Rust uses the same ABI as C (it does).
> EDIT: I left out the BIGGEST point -- a dealbreaker. Python does NOT have an ABI. It has an API. AFAIK, that means you have to write a Rust ABI-compatible wrapper for EVERY VERSION of Python.
That is unfortunate, but someone could write a crate that dynamically determines which Python is in use and chooses the right functions based on that, put it on Cargo, and everyone could link against it.
Anyway, people are already writing Python programs that call into Rust via ctypes and whatnot, so this hasn't stopped people yet.
> So if you just want to speed up an existing Python program, I would still recommend using a simple C or C++ extension. This solves both single-threaded speed issues and give you parallelism with multiple cores, so you will get huge speedups.
At the cost of memory safety, which is a big deal. And you have to learn C, which for a dynamic language programmer can be a lot harder than learning Rust.
Since you mentioned multicore, I should mention that the threading facilities available "out of the box" in C and C++ are much harder to use than the equivalent ones available in Rust, and if you reach for something like TBB and Boost now you have all the build system issues you described previously.
You can try to cover them up with magic code generators and version wrappers, but in my experience those are precisely the leaky abstractions that make people scared of FFI. They work 99% of the time, then bite you 1% of the time. The built-in Python FFI is somewhat ugly, but simple and debuggable.
I'm not saying you shouldn't use Rust for Python extensions -- just that there is an inherent awkwardness in doing so, given that Python is written in C, and the Python 2.x has no stable ABI. Of course Rust offers benefits, so if you want to pay the cost for those benefits, it might be worth it.
I don't know about OCaml, but Rust (supposedly) will interface perfectly fine with anything providing a C API (which includes Python, along with quite a few other scripting languages from that era, like Perl and Ruby). Rust's ABI-compatibility with C is one of the primary selling points.
I don't know off the top of my head if anyone's tried writing Python extensions in Rust, but I don't see why it wouldn't be possible to do so with at least as much capability (if not more) as C/C++.
The talk above was on the front page last week some time and describes one method to write Python extensions in rust.