I don't know if it brings anything new on the table. Also, I am not convinced that Ocaml is state-of-the-art in term of development productivity, relase-management, debugging, etc. Anything any non-trivial project could want. Language features is not the only thing we need.
Yes, the OCaml tooling has really improved recently, and the OCaml workflow has been more and more smoother. Still. I wont recommend it for a business.
So, if the original author read this, could you answer these questions about how do you ship products with Ocaml:
- How do you do Quality Assurance ? (anything from unit-testing, integration testing, functional testing, etc. I guess you have to do it a lot to check that Gmail didn't break its integration). Testing in isolation has its share of challenges in Ocaml.
- How do you manage your builds and releases ? Private Opam repositories ? Directly shipped to Google ? Do you have beta/staging channels ?
- And last but not least, what have been the pain points so far and have you been able to fix them or do you just work with it ? (it happens with any tech stack, but it's good to know what the trade-off are)
I am very curious how this could work at "entreprise-scale" and I would be glad to have some real world examples of ocaml in production.
"Functor" might just be the most overloaded term in computer programming... Just of the top of my head it has totally different meanings in Ocaml, Haskell and C++.
They're not totally different between OCaml and Haskell. They're based on the same concept from Category Theory: a mapping of objects and morphisms from one category to another. It's just that Haskell functors are at the type level and OCaml's functors are at the module level.
Apparently F# has support for neither style of functor--it doesn't have parametric modules and it also doesn't have typeclasses. So in F# `map` is defined independently for each type:
Set.map : ('a -> 'b) -> 'a Set -> 'b Set
Seq.map : ('a -> 'b) -> 'a seq -> 'b seq
List.map : ('a -> 'b) -> 'a list -> 'b list
Array.map : ('a -> 'b) -> 'a [] -> 'b []
F# is an excellent technology, and inspired some of the recent Ocaml developments (we still look at ActivePattern with envy).
I saw some pieces of F# in the FinTech here and there, I think Microsoft could do a better job at marketing it because it really has great potential in the .Net ecosystem.
I'm not sure why these things require state-of-the-art technology.
Like, testing is basically just checking a bunch of if statements. You can use fancy frameworks that color your tests red and green, but other than that kind of thing, what's the actual problem? Why do you say testing in isolation is particularly challenging with OCaml?
It's been years since I used OCaml and I don't know anything about OPAM so I'm also interested in the answers.
Did you try to use OCaml and run into a bunch of difficult problems?
> Did you try to use OCaml and run into a bunch of difficult problems?
Yeah, I am a long date contributor and user of ocaml for little projects (mostly compilers and AIs). But not professionally.
> It's been years since I used OCaml and I don't know anything about OPAM so I'm also interested in the answers.
Opam is Bundler done right. It manages ocaml toolchains and packages dependencies. It's easy to pin a specific version or publish your own.
A setup oasis + ocamlbuild + $editor could go quite far. After that, it will depend of how you works and what you are developing.
> Like, testing is basically just checking a bunch of if statements. You can use fancy frameworks that color your tests red and green, but other than that kind of thing, what's the actual problem? Why do you say testing in isolation is particularly challenging with OCaml?
That is a very interesting question that deserve a blog post on its own. But I don't have the time nor the patience to do so, so let it be the HN comment.
> Like, testing is basically just checking a bunch of if statements.
Wrong ! There's multiple kind of "if statements to test";
-1- I want to test that `my_inner_fibo(0, 1) == 1`. This is unit test. Basically a transcript of the technical specifications into assertions. This is the easiest test, the most verbose, but it's also the kind that test the least.
-2- I want to test that `UserModule.retrieve_orders(user, command)` makes the good calls. This is integration testing in white box, which test that the API contracts between you inner interfaces are respected (which is also part of the technical specifications). I don't find it very useful but it's better than nothing when you can't do more.
-3- I want to test that, with a given state `user`, and `command`, when I call `UserModule.retrieve_orders(user, command)` I get exactly this object. This is integration testing in black box. This is useful to find errors in inner logic a group of modules (but you don't know immediately what gone wrong). As the call stack could be very large (and so the scope of what you are testing), you want to reduce the moving parts and replace some of the modules with trivial mocks. Integration testing in a black box test a lot of things and is very useful to find bugs.
-4- I want to test that a call to `$./my-binary ctl command --flag=true` have a specific behavior. Could also be a network request to a server, a message to a deamon, a click on a GUI... Anything exterior to the program. Here we test the behavior. This is what the user will use, that's why it's functional testing. This could break often (in the case of a GUI) or not (in the case of a CLI binary). You should always do functional testing if you respect you users a little.
All these kinds of testing have different requirements, and some need a perfectly controlled state to be created. The issue is not in the conditional testing, but in the setup this perfect controlled state, which sometimes need to make the code believe it use the good module, but you gave him a stubbed one which does trivial work, or signal you when something happens.
I don't know any simple way to spy on functions without using the MirageOS design which heavily use Functor injunction. It's quite an academic way of doing it.
Thanks for the detailed response. I've used mocking frameworks in Java and while they can be pretty helpful it seems like the basic functionality is pretty easy to get at, for example heavy modules like database layers could be module parameters, or functional parameters, or however you want to structure it. Surely language features and cutting-edge test frameworks can make all this stuff easier, but to me it seems like it can all be done easily with the normal ways of doing abstraction (and OCaml is pretty good at abstraction).
Just as an aside, white box testing (#2) shouldn't be used for testing contracts between your inner interfaces, but rather, your consumption of some external interface that is hard to set up.
As an example, I want to make sure that when I hit a REST API endpoint, I do all my logic and get a proper return value (black box testing), maybe that the state of some bit that I've already set up is correctly changed (say, the database, by then checking it as part of the test to ensure the thing I said should be written was written), but that I also send a server sent event (SSE) to connected clients indicating the change. I don't want to actually have to have opened up clients that conform to the SSE specification as part of my integ tests (because that's a pain), so instead, I'll just assert that the library call to send that event does indeed get called with the right thing. From there, I can test once, that the library call does indeed lead to an SSE being sent to the browser (and can even write a full environment integ test with Selenium or something), and from then on, my single system tests just assert that call is made when it's supposed to. I'm effectively integration testing without implementing/mocking a connected user to test SSEs.
Similar things can be useful when putting items onto external queues, making calls to foreign interfaces, etc. You should never make assertions about the path through your code a call takes in white box testing (because refactoring can change those, and you have increased your test burden for little reason), you should care about side effects you can't easily otherwise test.
The IDE situation is now excellent (thanks to merlin[1]) and OPAM, the OCaml Package Manager, is the best package manager I have ever used.
The remaining pain point, as far as tooling goes, is the debugging situation, but steady progress has been made and it should receive very large improvements in the next OCaml version or so.
Kind of. It's a bit awkward to get it running on Windows, but somehow (by accident) I managed to do a fully functioning installation on Cygwin. That said, many of the packages have still not been ported to Windows, so my recommendation is to set up a headless VM of some GNU/Linux distro and SSH into it.
Well, I'm not sure I would go as far as "excellent". There is no "OCAML IDE", there is vim/emacs + tools. You can hack an IDE-like workflow by way of inotify, but if you want the "works-out-the-box" experience, that's not going to happen. As you mention, debugging is lacking, and more generally Merlin can't do much in the way of refactoring.
If you're fine with, eg, hacking Python in VIM, you'll be pleasantly surprised by the OCAML situation. If you live in an integrated IDE, there will be some adaptation.
I'll tell you how we do these things in the libguestfs/virt-tools project [1]. The project is written in a mix of C, Perl and OCaml. Mainly C is used for the low-level/library bits, and OCaml is used for the higher-level tools.
- QA: We have some unit tests, but mainly we use a huge test suite that does end to end testing of tools. It uses automake's test framework, so it works across all the languages in the project.
- Builds and releases: We use autotools and tarballs. It's automated using a thing called 'goaljobs' which is like a generalized make.
- The pain points for us all derive from autotools itself, which is both crap and better than all the other build systems[2]. It is at least well understood.
I'd also say the killer advantages of OCaml for me are: Easy calling into C, and compiles to a native binary.
[2] I use the term "build system" in a rather narrow sense of something that (a) runs on Linux (b) lets the end user download a tarball and (c) builds using ./configure && make
I'm always very amused when people say OCaml can't work at "enterprise scale" (let me snort a bit on that one, considering the scale of some open source projects) given the amount of evidences of the contrary[1]
To answer one of your question. Yes, we have testing frameworks, both for unit[2] (inline[3]) testing and property testing[4]. As for the rest, it's not OCaml specific at all. :)
> I'm always very amused when people say OCaml can't work at "enterprise scale"
Yeah, you will notice that quotes that show you shouldn't take it too literally. ;) (also I have no doubt that Ocaml can work in a business. I just saw no one speak about it appart Jane Street, so I am curious)
What I was meaning that "a team with a hierarchical organization, not-ony-geniuses, and more than 4 people". Few of the serious Ocaml projects (if any) fall in this category, which is common for all businesses.
About the testing tooling, I am well aware of the state of these technologies. I even contributed to some of them. Sorry but it's light. It doesn't cover all the spectrum of what you'll want to test in a product.
Let's take your (excellent) libs and see:
- OUnit: unit tests
- QCheck: unit tests
- iTeML: unit tests
Where is integration and functional testing ? Unit Testing only test a very strict subset of the "does this work as intended" question. I recommend you to see how some projects do their testing. For example any classic Rails project. You could be surprised.
Some open questions I struggle myself to answer correctly:
- Mocking modules without having a build mess with oasis
Not sure about the other issues, but I believe this one is easily solved:
> Mocking modules without having a build mess with oasis
OCaml has parametrized modules (aka "functors"), which provide a very clean solution, compared to mocking whole modules anyway.
As a side note, I usually see mocking being used to test badly structured code. Refactoring that is usually a benefit for testing as well as a benefit for the code itself.
Here is how I handle this at my company. LibreS3 is a product written in pure OCaml [1], using as a backend a cluster running Skylable SX (written in C)[2]:
- QA: unit tests written using oUnit, and integration tests by using linked Docker containers.
- builds/releases: opam + packages written for Debian and Fedora based distributions. The packages provided on our website are built inside Docker just like all the other packages. Internal beta packages are uploaded to a separate volume/bucket and served via LibreS3 itself.
- Pain points: building a package compliant with packaging policy is more complicated because I usually need newer versions, or OCaml libraries that are not yet packaged.
I planned to use opam to generate templates for LibreS3+dependencies but was waiting for the C backend to get packaged upstream first.
My slides from last year list a few more problems that I encountered during development but they aren't an issue currently [4].
The main advantages of OCaml for me are:
- the availability of high-level libraries that makes implementing HTTP APIs simpler (Ocsigen, Cryptokit, atdgen, etc.)
- event-driven/non-blocking architecture to support large number of concurrent users (Lwt)
- native binaries and static type system
- not having to look for certain bugs in my code like uninitialized variables, NULL dereferences, memory leaks or memory corruption bugs; which (used to) take up a significant amount of time when developing applications in C (although tooling on C side have improved with valgrind, clang -fsanitize= and Coverity).
That doesn't mean that code written in OCaml or the libraries that I use doesn't have bugs, I track them down
and provide patches just as I would for a C library.
Although perhaps writing code in OCaml has made me somewhat overconfident in my code, and I find bugs in other people's code more easier than in mine even if I'm not looking for them.
I'm not a user of OCaml, but here is a counter-argument for C#:
First-class functions
C# has closures, and can pass function pointers with delegates.
--
immutability is the default
Mutability can be a useful tool in some situations, but it is often unnecessary and potentially harmful.
I think this point has probably been argued to death by now, but I still can't see this as a good idea. 'Side effects' are really just a reality of software development. For example if I want to fade in a GUI element on a page, I don't want to create a new element for every transition change. Theres nothing wrong with mutable state, but it is nice to have immutability for some use cases. In this case c# has valuetypes, and the const/readonly modifiers.
--
Strong static type checking
Yep C# has had this since the beginning. And you can even use 'dynamic' if you really want which moves all type checking to runtime.
--
Algebraic data types and pattern-matching
I feel like these are more buzz words than actual substance. The Html example given also doesn't seem exactly clear to me how the operations are being performed. Here's a pseudo c# way of doing it:
the typical object-oriented implementation of this in Java would involve multiple subclasses of a common base class.
I assume they mean that all element types would inherit from a base IHtmlElement? This seems like a good thing to me.
--
Type inference
Again C# has these abilities with 'var a = 0' for example. It won't infer method return types, not because it couldn't, but because it's just generally clearer to have the explicit return type written there in the code for the reader.
--
a replay-capable debugger
C# has intelliTrace for this. Though I expect both suffer from the same problems, slow execution while in use, and huge memory footprint.
> I feel like these are more buzz words than actual substance.
Um, both those concepts (algebraic data types and pattern matching) are very clearly defined, classic language features. Why would they be substanceless buzz words?
When I think of 'pattern matching' I think of some complex idea like brain-level parsing of text to an AST via matching of syntax patterns. But what OCaml pattern-matching really is is just the equivalent of a C# switch statement, or if/else-if statements.
You might think of that but that's not what it means in this context. ML-style pattern matching is a concise way to match nested structures and simultaneously bind variables to arbitrary subelements, which is particularly useful when combined with algebraic data types. Both are classic features of ML and derived languages since the early 70s. They're not buzzwords just because you don't know what they mean.
You're right that pattern-matching in functional languages resembles a switch statement. In a way it generalizes the concept of a switch statement, in the sense of "doing different things based on the shape of the object at runtime." However, it's quite a bit more powerful.
Here's a simple and oft-used example, which is option types. They correspond roughly to nulls so let's compare two blocks of pseudocode:
// traditional
if myValue == null:
print("nothing here!")
else:
print("value is " ++ myValue)
// with pattern-matching
switch myValue:
case None:
print("nothing here!")
case Some(thing):
print("value is " ++ thing)
The two appear similar on the surface, but in the first example, we are relying on the programmer to check that myValue is not null. If he or she fails to check this, we'll likely throw an exception or worse. On the other hand in the second case, we do not need to trust anyone; instead we guarantee that there will be a non-null value added to the string, because it won't be possible to "extract" the inner value from myValue without entering a context in which myValue has a `Some` form. There is a lot more safety this way, since it's guaranteed by the language rather than the programmer. And not only is it more safe, but it's a lot clearer and more expressive as well. Consider a type with more than two variants, or holding multiple internal values rather than just one. The imperative if/then/else solution rapidly becomes verbose, obscure and error-prone, while the pattern matching case is just as straightforward as the above example.
It might seem weird on the surface but after a few times using it it's the simplest thing in the world and you'll wish you had it in other languages. In fact, Rust, which isn't (primarily anyway) a functional programming language, makes heavy use of pattern matching.
One of the things i love about Erlang apart from the Concurrency model is their pattern matching ability. When you are parsing a lot of "structured" data, matching the data-structure and having it parsed into variables at the same time gives me that warm fuzzy feeling :)
C# has actually been trying to incorporate as much from ML languages and Haskell as it can (look at Linq, for instance). And of course, F# is an ML family language with a strong resemblance to OCaml that runs on the CLR. Trying to attack OCaml by noting that C# has a lot of the same features kinda misses the arrow of causation here.
Did you even read the article? OP says in the second paragraph OCaml includes many features that are not available in the more mainstream programming languages listed above, which included C#. My post was to show that C# does in fact have those features, not to argue the origins of said features.
That's true though. You argued why they're not necessary but not that they're there. And C# still lacks a solution to the ML module system, GADTs, and other advanced features of OCaml.
It is, but it is worth noting that when OCaml or Haskell programmers talk about type inference they are usually referring to global type inference which allows an argument's type to be inferred from its uses. For example:
They can call it any way they fancy, something like "Majestic Royal Type Procession" would have been equally ambitious. But in a context of ML, type inference is a Hindley-Milner inference or an equally powerful equation solving system, and not just a pathetic "assign the RHS type to LHS blindly" as it is in C++ and C#.
It's easy to underestimate what the advocates of functional languages are talking about. One can pass a function (pointer) to another function in C for example. Lots of languages have library support for pattern matching via regular expressions. Types are supported in various ways in most programming languages. However, these things aren't even close to what is being talked about here, and that is why the comments are being down voted.
If you are interesting in studying and comparing programming languages, I suggest picking up one of the five big functional languages: F#, OCaml, Haskell, Scala, or Clojure. Scala and Clojure are great, they run on the JVM, they perform well, are practical and pragmatic, but are probably not the best way to learn "functional" programming because they include too many additional features or leave out some features that some consider important. I would recommend starting with Haskell and then comparing it to the others to see if one of these could be helpful in your own programming. There are good, free resources for learning functional programming, see [1].
> I think this point has probably been argued to death by now, but I still can't see this as a good idea. 'Side effects' are really just a reality of software development. For example if I want to fade in a GUI element on a page, I don't want to create a new element for every transition change. Theres nothing wrong with mutable state, but it is nice to have immutability for some use cases. In this case c# has valuetypes, and the const/readonly modifiers.
Mutability is one side effect. OCaml is not Haskell and doesn't have a type system baked in. Sure, sometimes you need to cheat, but mutability as the general rule is crazyland. Immutability gives you a compile-time assurance that your data structures are consistent and valid, as long as the tests in your constructor function are correct.
> Yep C# has had this since the beginning. And you can even use 'dynamic' if you really want which moves all type checking to runtime.
Well, it has strong static type checking... within the bounds of what its type system supports. Which is fairly limited.
> I feel like these are more buzz words than actual substance.
Others have responded to that, but in short, your impression is wrong.
> Again C# has these abilities with 'var a = 0' for example. It won't infer method return types, not because it couldn't, but because it's just generally clearer to have the explicit return type written there in the code for the reader.
C#'s "type inference" is cute, but you can't seriously compare it with OCaml's.
C# developer here, with a long history of OCaml development (and I'm still using OCaml knowledge as a filter for recruiting C# developers).
First-class functions in C# are nice, nearly as good as those in OCaml. The main annoyance is the incompatibility between Action<> and Func<> (and, in general, the fact that C# uses void rather than unit).
--
Immutability: for every mutable class in our code base, there are three or four immutable classes, and `readonly` is likely the most frequent keyword. Among these, we have an entire hierarchy of dozens of classes that are entirely immutable (and make heavy use of immutable containers from Microsoft) because it's still the easiest way to implement a multi-reader, single-writer thread-safe data structure. You can survive without immutable-by-default, you can survive without OCaml's
{ record with a = b ; c = d }
syntax, but it gets very, very annoying to work without
{< self with a = b ; c = d >}
in an object-oriented language.
Algebraic data-types: I'm mostly writing compilers (yes, in C#, don't ask), and instead of writing a simple
| SetLineOp of param * param * index
I have to define a new class, three readonly fields, a constructor to set them, equality operators (R# helps, but still...), and five or six lines of visitor pattern boilerplate in order to get the "you missed a case" warning in pattern matching. This gets very annoying very quick, and there is no practical benefit to having all those classes here.
--
Type inference: C# does manage to be fairly clever, although not as clever as OCaml. I have seldom found it to be lacking in general use, aside from the ability to define local functions (var f = a => { ... } does not work, you have to spell out the type of f). There have been a handful of times when I was trying to do something very clever and found myself limited by the type system, and had to write annotations myself.
And one time, I had to reimplement GADTs with generics and reflection. It was painful, but it works.
However, with a few minor tweaks (e.g. an option type), I would rather have the C# type inference than the OCaml one. The reason is that, if I want to do something very clever, I will not find myself limited to code that I can actually prove to the OCaml compiler as correct: I have, time and time again, resorted to reflection and code generation to work around such situations. In other words C#'s Obj.magic is a lot more powerful (and safe, and expressive) than OCaml's.
A fairly good example is Eliom's way of expressing the parameters of a service. In C# you would write in a PageController class
public Details Update(PageId id, UserId user, [PostBody] Details body)
and have your web framework automatically bind this to POST /page/update/{id}?user={user} with the appropriate serialization for PageId and UserId. And writing such a framework is easy: a couple hundred lines of code, with run-time type safety.
Another naive arguments for why language X is better. Native and 3rd party libraries+frameworks far outweigh usability and productivity gains than few cool language features. Most features mentioned in article are available in mainstream languages at certain extent that is acceptable for professional development at scale. This is not to make case against OCaml - it is nicely designed language indeed - but the case should be made from beauty and "as matter of taste" perspective rather than productivity and business justification perspective.
Pattern matching, strong typing with type inference and mutability by default are certainly not available in "acceptable" mainstream languages. Also OCaml is highly productive, very fast to run, and you can just call down to C when you need to use a library.
More precisely, OCaml allows me to refactor my code very quickly, finding almost all mistakes even before any testsuite runs.
This is possible due to a combination of language features which only few languages have (notably, Haskell, SML and F#).
Not sure how this goes into total productivity, but this acceleration in development speed is something that no number of frameworks or libraries can match up.
Productivity gains by well tested vast number of libraries and frameworks far far outweigh gains due to language features such as pattern matching. Sure you can drop down to C from pretty much any language but most setups attempt to minimize that because maintaining hybrid language infrastructure (build/unit tests/upgrades/logging/auth etc) is typically expensive over long run. Also, may be calling libraries may be ok but using entire frameworks is much harder to use cross language.
Except that it's easy to call down to C from OCaml so you precisely do not have to maintain that sort of infrastructure. OCaml also has a large number of native libraries of course.
Imagine you have to write an optimising compiler for some exotic DSL, targeting a custom embedded platform. All the hipster "frameworks" and "vast number" of irrelevant shitty libraries won't help at all, while pattern matching alone will be extremely useful.
> Productivity gains by well tested vast number of libraries and frameworks far far outweigh gains due to language features
I see this argument all the time. I don't know if the type of software development I do is special (I doubt it, it's fairly standard business software), but the amount of time I spent on business logic dominates total development time.
Past a certain fairly low threshold of core libraries (HTTP, JSON, CSV, database bindings, standard stuff like that), the amount of time libraries can save you is dwarfed by the cost of refactoring all that business logic.
On the other hand, if the language is so niche as to lack the basics, you can find your entire development budget spent on building functionality other languages offer out the box. I don't think OCaml is that niche...
> Native and 3rd party libraries+frameworks far outweigh usability and productivity gains
It heavily depends on what you're doing. You might be surprised, but there is a lot of projects which do not rely on any 3rd party code and are built almost entirely from scratch.
In other words, they like the fact that it has features typical among languages oriented toward functional programming.
There's no cost-benefit analysis here discussing the obvious pitfalls from a business perspective, such as a much reduced talent pool and probably higher salaries for the few programmers who are comfortable working with such an uncommon language. There's also no discussion about why they believe a business offering a calendar application needs to use such a technically demanding language. This would be more understandable for, say, Jane Street (high-frequency trading), but for this particular business my naive belief is that this probably will not be the "competitive advantage" they claim it is.
What are your reasons to believe the job market for hippy functional languages is charcteristically highly paid and a "seller's market"? Judging from some anecdotes and the proportion of FP programmers working in academia I would have thought the opposite. Unless you're looking for the shallow end of the talent pool...
My prejudice is that anyone competent in functional programming is also comfortable with other programing paradigms, in particular OOP. Not so much the reverse. Thus the limited talent pool and higher wages.
> My prejudice is that anyone competent in functional programming is also comfortable with other programing paradigms, in particular OOP.
That might be true, but if they are competent in both, that's often because they have switched from OOP to FP and prefer the latter nowadays so if there is a selection between otherwise equivalent jobs, they would tend toward the FP job.
Perhaps, as you suggest, attracting more talent is one benefit of using an FP-oriented language for their codebase. It would have been nice to see this claim evaluated in the context of a cost-benefit analysis including the costs. This article, which claims that OCaml provides a "competitive advantage," doesn't provide that. Thus I don't believe it is a useful source of information for evaluating the merits of OCaml in particular, or FP in general, in a production environment.
virt-v2v[1] -- written in OCaml -- had a bunch of contributions from people who mainly work on Java for their dayjobs. The main problem is that people are close-minded and refuse to learn anything new. If you can filter those people out with your hiring processes, then OCaml is not difficult.
Not mentioned in the article, but Paul Graham's Python Paradox (http://www.paulgraham.com/pypar.html) could a factor. So maybe it's harder to find developers comfortable programming in OCaml, but these developers are a self-selected more refined group. Whether that makes sense for the business at hand is another question.
Actually, I wasn't. The first claim is that there are fewer programmers using OCaml because it's uncommon in industry. The second is that their business has no obvious need for a paradigm which, in my view, requires more education and a stronger understanding of mathematics. I'll admit I don't have any evidence other than personal experience to support the second claim, but the first is easily verified.
Seriously, I'm quite baffled at the idea that OCaml would be technically demanding. I was taught OCaml in college, along with C, Java, C++, and Prolog.
Ocaml was easier than even Java. Much less demanding than the minefield that is C++. And it certainly doesn't require more education. A different education, that's for sure, but not more.
As for requiring a stronger understanding of math… Languages like OCaml just don't pretend that programming is not math. So it tends to scare away those who fled math in the first place. C++ and Java are just as mathy (if not more, just see the sheer size of the specifications), but they don't really tell you.
<Grumpy Dijkstra> Programming should have been of the applied mathematics department, not the electronic engineering one. </Grumpy Dijkstra>
I really don't disagree with the substance of your comment, though the Algol-family languages tend to be hard because of disorganized core libraries and occasionally cumbersome syntax rather than because they depend on unfamiliar mathematical constructs.
I have little experience in education, but my naive hypothesis is that it would be easier to teach a child, say, Python than any FP-oriented language. I suppose it goes without saying that if children were introduced to the lambda calculus beforehand this might not remain true.
See my comment beneath the root to see my clarification of the original post.
I don't think Dijkstra would disagree with me. After all, it was he who suggested the average American programmer would benefit "more from learning, say, Latin than yet another programming language."
The (destructive) assignment is an extra concept in imperative programming compared to functional programming.
It is also a form of resource management and incur resource management issues (do I need that value in this variable? Can I overwrite it and discard old value?).
The implicit state also is an implicit communication channel between different parts of program. In FP this communication is much more explicit, especially if you use something like Software Transactional Memory.
I think I listed enough points for you to reconsider your stance about Python being easier to grasp.
I know. I was surprised to find that my manager hadn't taken FP, while I had during coursework in computational biology at such a school. I think this just illustrates my point: in industry-oriented programs, FP isn't considered a useful tool. Right or wrong, it's the current reality.
Yes, FP is uncommon, though it's gradually becoming less so.
However, I'm puzzled about your remark about industry-oriented programs. I'm not sure what "industry-oriented programs" even means, but assuming you actually meant "FP doesn't have widespread use in the industry", I'd say that while this is true, a- it's becoming less true every day, through the use of mixed-paradigm languages, and b- even the purer FP languages see industry use.
"Right or wrong" this current reality you mentioned is shifting. Hybrid languages are becoming more common. Traditional languages are adopting functional idioms (and idioms from other paradigms as well!). It's not true that there is a clear-cut "natural" kind of programs for which to use FP; the area for which FP is good is pretty much good old "let's write software that works and has few bugs". If you have this goal, you can use FP to your advantage.
Any success story of someone using OCaml for this is welcome.
By industry-oriented programs, I'm referring to institutions of higher learning which have tailored their cirricula primarily to prepare their students for employment in industry. I contrast this with those which are intended to prepare their students for research.
I've said nothing of depth about the merits of OCaml or FP in itself. I'm simply pointing out that the blog post clearly identifies the use of OCaml as a competitive advantage without discussing any of its drawbacks, whether these arise from the nature of the language itself or for historical reasons. This makes the article significantly less useful for a person trying to select a language for their company's codebase, which I take to be the whole point of writing the post to begin with.
Perhaps the real goal of the post was to attract the attention of OCaml developers, rather than discussing the titular question "Why we use OCaml." If this is the case, I'm sorry for having wasted my time criticizing it.
It's a piece of advocacy. I wouldn't expect it to discuss downsides much. What irks me more is the absence of meaty arguments for the language.
But then I realised that explaining why the listed features are good is hard. Someone who haven't been exposed to FP is going to take a lot of teaching and persuading. Or a damn good motivating example, but I have yet to find one.
Oh, I misunderstood your comment then. I suppose you're right, but I think industry-oriented school programs will start featuring more FP in their curricula as hybrid languages advance even further in the real world.
Like a sibling comment said, this is an advocacy piece. I don't know if anyone will pick OCaml (or any language) for their company immediately after reading a blog, but it might pique their interest and make them research the language and consider it for their own projects, an interest which might eventually develop into considering the language for their day job. I know it was the case for me with Scala!
I agree it's uncommon. I disagree it's technically more demanding, even though that's the folklore surrounding FP (technically more demanding than what, anyway?). The need for programming paradigms is seldom "obvious", but I'd say the need for software with fewer bugs and good performance, written in a language developers enjoy using, should be self-evident.
I can understand the availability problem created by a reduced talent pool but I didn't know getting cheap labour was a top priority in when hiring quality software engineers.
Really, this "oh no a foreign language" thing is way off. Every good software engineer I know would not bat an eye to learn a new language.
Ocaml is not quantum physics, it's pretty understandable language.
Since a few people are kind of getting on my case, I'd like to point out that I learned OCaml for coursework in functional programming and computational linguistics. I'm not suggesting it's useless or comparable to quantum physics in difficulty. I'm only pointing out the reality that the majority of software developers have little to no exposure to functional programming. Few of them will be prepared to commit to a new programming paradigm when there are so many other employment opportunities which use more familiar frameworks and develop skills more widely applicable in the job market.
I'd also like to suggest that, for certain applications, the most touted benefit of functional programming (the ease of proving algorithms behave as intended) is not especially relevant. I would even dare to suggest that, in some cases, it may be more rational to hire a "decent" programmer than a "great" one.
This is to say nothing about the specific language features of OCaml or the utility of functional programming in general, just that the benefits need to be weighed against the costs. In an article claiming that using OCaml provides a "competitive advantage" from a business perspective, I would have expected a discussion of both.
OCaml is a nice language and it surely has advantages. But don't treat that as a competitive advantage. All of the benefits mentioned can be easily found in other programming languages, except for maybe pattern matching, but that can be easily replaced with similar constructs.
Using a not very popular programming language can actually make it harder for you to hire people.
>> that can be easily replaced with similar constructs
> Mind giving an example?
You can use, for example, elaborate if-else constructs that firstly try to
determine type of the value, then check for values.
I've never seen in imperative languages anything similar to conditional that
checks both type and content of an expression, and allows to capture parts of
that expression into variables. Well, except for precisely the pattern
matching construct, where it was implemented deliberately after functional and
declarative languages (Rust).
>> Using a not very popular programming language can actually make it harder for you to hire people.
> Do you really want to work with people who can't learn a tiny little language?
I see things a little differently. It's extremely difficult to find an idiot
who can write OCaml, while it's fairly easy for C++, Java, or C#.
Also, allowing people to learn OCaml and use it every day on the job is
a big plus for a company. It's way better than free sandwitches for breakfast
in the long run.
Oh, look, non-exhaustive checks are exactly as with if-else. And no, `match ..
with' cannot be calculated in compilation time typically. Poor argument.
It's being a combination of destructuring, branch selection and variable
assignment that makes pattern matching superior.
You can also do it in Brainfuck if you like, with an equivalent code bloat. Pattern matching is an order of magnitude more readable and dense than the if ladders.
As for the hiring, it is a silly idea in general to seek "programmers in XXX language" (and to position yourself as an XXX programmer). Hire programmers. Just programmers. Languages are irrelevant.
> You can use, for example, elaborate if-else constructs that firstly try to determine type of the value, then check for values.
That's dynamic typing. Very different from pattern matching over sum types: with dynamic typing, you can basically forget about most compile time guarantees. Seriously, you sound like, "why bother with Ocaml when JavaScript does the same thing?". I can tell you from first hand experience that they do not feel the same at all.
The other classic example, class hierarchies, is also very different from pattern matching —and much more cumbersome. The only language I know of that kinda bridged the two is Scala, with case classes.
With pattern matching you'll get static checking if your matching is exhaustive. Impossible with an if ladder. Therefore they're not semantically equivalent.
Statically checked pattern matching validates that the provided patterns cover all possible values the variable could have (the whole space of the variables declared/inferred type). So the actual runtime value is not necessary: if the match is exhaustive, there is no possible value that could be taken at runtime that would result in no match.
Why would you care about selecting the branch in compile time? We're talking about language features semantically equivalent to pattern matching, and, turns out, there are none. As for the importance of the exhaustion check, see the billion-dollar mistake.
Your solution with type and value checks in an if ladder was called "dynamic typing" exactly for this reason - you select paths dynamically in the runtime without any static checks on soundness of this selection.
> Why would you care about selecting the branch in compile time?
I wouldn't. Pattern matching is primarly a conditional, that's why I focused
on if-else.
> We're talking about language features semantically equivalent to pattern matching, and, turns out, there are none.
Why would you think that pattern matching revolves around compile-time
guarantees? It does not. It's primarly a conditional, everything else is an
optional, additional effect. You can have pattern matching in dynamically
typed languages (Lisp, Erlang).
From what I've heard, Jane Street mainly (or at least often) hires developers who have never before used OCaml or even functional programming in general.
> Using a not very popular programming language can actually make it harder for you to hire people.
This is a very popular opinion, but I never see anyone taking the time to bring actual arguments. Do you have any experience hiring people or are you just spreading this seemingly self-evident claim?
If a language is popular, wouldn't more companies want to hire for it, which would make recruiting harder? See http://blog.activelylazy.co.uk/2013/05/27/choosing-a-program....
Also, being popular means that it is being known by a wider audience of people: isn't it harder to filter through all the applicants who say they can write in the popular language?
As someone currently hiring for a mid level popularity language (ruby) I find that we don't have as many applicants as we'd like and that many people who apply have ruby on their resume but have never done an actual project in it.
We also do javascript and, despite its popularity, we also get quite a few applicants who have it on their resume and have never used the language for anything useful.
Personally, my opinion is just hiring is hard. It is a very valid point though that if you are not willing to hire remote (which my company is not) you are very limited when trying to hire for less popular languages. Alternatively just train people, but that can be expensive.
Using a not very popular programming language can actually make it harder for you to hire people.
That's a non-sequitur! The "unpopular languages make it harder to hire programmers" myth has been debunked many times right here on HN. Just hire smart people, not blub programmers.
> Using a not very popular programming language can actually make it harder for you to hire people.
The type of people who go about learning OCaml are arguably on the whole more competent and passionate than your average run of the mill Java programmer. Using OCaml might be a good way to separate the wheat from the chaff.
I think you missed one of the most compelling things about OCaml: it's very arguably most performant system in existence which also provides the user with a strong type system. C/C++ provide performance but not a strong type system, which results in huge numbers of bugs. Other languages provide strong type systems, but in exchange give up a lot of performance. OCaml is somewhat unique in this combination of features.
I say "somewhat unique" because there are other languages in this space: Rust in particular may be the language of choice for these feature needs in the future.
Anecdotally, OCaml seems to often be faster than Haskell, though not necessarily materially so. More important though (in my opinion at least) is that it seems performance is more predictable in OCaml.
High performance code in Haskell tends to rely on a combination of stream fusion, rewrite rules firing and judicious use of unboxing and bang patterns. I've heard it said that the OCaml compilers simplicity makes it far easier to know what code it's going to produce.
Disclaimer: I'm a Haskell user, so it might just be that I'm more familiar with Haskell's warts.
It's worth noting as well that the flambda branch of the OCaml compiler is starting to include lots of these advanced optimizations which will (a) probably improve performance, (b) make abstraction more "free" enabling more use of it, and (c) put a hit on performance predictability again.
These questions often depend on how much time you are willing to spend making things fast. OCaml has an extremely predictable performance curve, which is nice when you are writing code that has to run fast. In turn, productivity is good because you don't get into situations where you have to rewrite code to make it faster in many cases. Even for brute-force algorithms, it tend to run fast enough that it would work well.
Over F#, OCaml provides a module system. This helps programming in the very large.
Over Haskell, OCaml provides a module system, and strict evaluation. The latter is a contended point, but proponents of OCaml claim it leads to memory/execution predictability. In Haskell (GHC) you often have to turn on optimizations in order to understand how your code will perform in reality, whereas byte-code interpreted OCaml acts just like natively compiled OCaml space wise, but is roughly 10 times faster in execution speed.
Currently you can deal with strictness using Bang Patterns. The addition of Strict and StrictData with -XStrict simply lets module writers cut down on that noise and communicates intent. It isn't meant to change or split the language Haskell in two; except for testing, you probably wouldn't go enabling it everywhere just 'cause.
Not trivially, no. Naive implementations in Haskell/F# are almost universally slower than naive implementations of the same algorithm in OCaml. In Haskell, this is partly due to lazy evaluation (which many production Haskell projects turn off) and in F# it's largely due to the baggage of interacting with the CLR (I have less knowledge about this, I'll admit).
I think it only makes sense to talk about naive, idiomatic implementations of an algorithm when you're talking about the performance of a language. If you start talking about highly optimized versions of algorithms, it's harder to make statements about which language is faster. People who use numpy adeptly get extraordinary performance in Python, but I don't think that makes the argument that Python is faster than OCaml; it's hard to say what an equivalent level of optimization in OCaml would look like.
I always thought OCaml looks like a language which should be fantastic for Erlang-style actor programming or Go-style network programming.
Every few years I take another look. Last time I researched this, I came across a bunch of incipient attempts at distributed, concurrent programming frameworks, but they were all abandoned around 2009. I couldn't find any mature, maintained frameworks, any easy-to-use concurrency primitives (lwt has, to my untrained eyes, a terrible API and is no match for goroutines or Erlang processes), and apparently the global interpreter lock is still there.
Pony looks nice, but unless it's something revolutionary I just don't have the energy or patience to be part of the first wave of anything new.
That's why I'm not, at this moment, using Rust or Nim in production. I want a mature toolset, a large, cohesive standard library, a large, active ecosystem, etc.
Go only recently reached this stage. Node.js has been there for some time. Elixir is there thanks to leveraging Erlang.
116 comments
[ 3.2 ms ] story [ 179 ms ] threadYes, the OCaml tooling has really improved recently, and the OCaml workflow has been more and more smoother. Still. I wont recommend it for a business.
So, if the original author read this, could you answer these questions about how do you ship products with Ocaml:
- How do you do Quality Assurance ? (anything from unit-testing, integration testing, functional testing, etc. I guess you have to do it a lot to check that Gmail didn't break its integration). Testing in isolation has its share of challenges in Ocaml.
- How do you manage your builds and releases ? Private Opam repositories ? Directly shipped to Google ? Do you have beta/staging channels ?
- And last but not least, what have been the pain points so far and have you been able to fix them or do you just work with it ? (it happens with any tech stack, but it's good to know what the trade-off are)
I am very curious how this could work at "entreprise-scale" and I would be glad to have some real world examples of ocaml in production.
https://realworldocaml.org/v1/en/html/functors.html
Apparently F# has support for neither style of functor--it doesn't have parametric modules and it also doesn't have typeclasses. So in F# `map` is defined independently for each type:
I saw some pieces of F# in the FinTech here and there, I think Microsoft could do a better job at marketing it because it really has great potential in the .Net ecosystem.
Like, testing is basically just checking a bunch of if statements. You can use fancy frameworks that color your tests red and green, but other than that kind of thing, what's the actual problem? Why do you say testing in isolation is particularly challenging with OCaml?
It's been years since I used OCaml and I don't know anything about OPAM so I'm also interested in the answers.
Did you try to use OCaml and run into a bunch of difficult problems?
Yeah, I am a long date contributor and user of ocaml for little projects (mostly compilers and AIs). But not professionally.
> It's been years since I used OCaml and I don't know anything about OPAM so I'm also interested in the answers.
Opam is Bundler done right. It manages ocaml toolchains and packages dependencies. It's easy to pin a specific version or publish your own.
A setup oasis + ocamlbuild + $editor could go quite far. After that, it will depend of how you works and what you are developing.
> Like, testing is basically just checking a bunch of if statements. You can use fancy frameworks that color your tests red and green, but other than that kind of thing, what's the actual problem? Why do you say testing in isolation is particularly challenging with OCaml?
That is a very interesting question that deserve a blog post on its own. But I don't have the time nor the patience to do so, so let it be the HN comment.
> Like, testing is basically just checking a bunch of if statements.
Wrong ! There's multiple kind of "if statements to test";
-1- I want to test that `my_inner_fibo(0, 1) == 1`. This is unit test. Basically a transcript of the technical specifications into assertions. This is the easiest test, the most verbose, but it's also the kind that test the least.
-2- I want to test that `UserModule.retrieve_orders(user, command)` makes the good calls. This is integration testing in white box, which test that the API contracts between you inner interfaces are respected (which is also part of the technical specifications). I don't find it very useful but it's better than nothing when you can't do more.
-3- I want to test that, with a given state `user`, and `command`, when I call `UserModule.retrieve_orders(user, command)` I get exactly this object. This is integration testing in black box. This is useful to find errors in inner logic a group of modules (but you don't know immediately what gone wrong). As the call stack could be very large (and so the scope of what you are testing), you want to reduce the moving parts and replace some of the modules with trivial mocks. Integration testing in a black box test a lot of things and is very useful to find bugs.
-4- I want to test that a call to `$./my-binary ctl command --flag=true` have a specific behavior. Could also be a network request to a server, a message to a deamon, a click on a GUI... Anything exterior to the program. Here we test the behavior. This is what the user will use, that's why it's functional testing. This could break often (in the case of a GUI) or not (in the case of a CLI binary). You should always do functional testing if you respect you users a little.
All these kinds of testing have different requirements, and some need a perfectly controlled state to be created. The issue is not in the conditional testing, but in the setup this perfect controlled state, which sometimes need to make the code believe it use the good module, but you gave him a stubbed one which does trivial work, or signal you when something happens.
I don't know any simple way to spy on functions without using the MirageOS design which heavily use Functor injunction. It's quite an academic way of doing it.
As an example, I want to make sure that when I hit a REST API endpoint, I do all my logic and get a proper return value (black box testing), maybe that the state of some bit that I've already set up is correctly changed (say, the database, by then checking it as part of the test to ensure the thing I said should be written was written), but that I also send a server sent event (SSE) to connected clients indicating the change. I don't want to actually have to have opened up clients that conform to the SSE specification as part of my integ tests (because that's a pain), so instead, I'll just assert that the library call to send that event does indeed get called with the right thing. From there, I can test once, that the library call does indeed lead to an SSE being sent to the browser (and can even write a full environment integ test with Selenium or something), and from then on, my single system tests just assert that call is made when it's supposed to. I'm effectively integration testing without implementing/mocking a connected user to test SSEs.
Similar things can be useful when putting items onto external queues, making calls to foreign interfaces, etc. You should never make assertions about the path through your code a call takes in white box testing (because refactoring can change those, and you have increased your test burden for little reason), you should care about side effects you can't easily otherwise test.
The remaining pain point, as far as tooling goes, is the debugging situation, but steady progress has been made and it should receive very large improvements in the next OCaml version or so.
[1]: https://github.com/the-lambda-church/merlin
If you're fine with, eg, hacking Python in VIM, you'll be pleasantly surprised by the OCAML situation. If you live in an integrated IDE, there will be some adaptation.
- you create and manage the build of your project with Oasis which call ocamlbuild nicely
- Opam works great with ocamlfind to manage the dependencies and the toolchain you use
- OUnit has backgroud workers for parallel testing
- Merlin and ocamlc annotations do wonders in term of semantic completion
- utop is an excellent toplevel with colors, completion and integration with ocamlfind so that you can load dependencies inside
All these technologies have greatly improved in the past 2 years, which is a really short span compared to the age of Ocaml.
- QA: We have some unit tests, but mainly we use a huge test suite that does end to end testing of tools. It uses automake's test framework, so it works across all the languages in the project.
- Builds and releases: We use autotools and tarballs. It's automated using a thing called 'goaljobs' which is like a generalized make.
- The pain points for us all derive from autotools itself, which is both crap and better than all the other build systems[2]. It is at least well understood.
I'd also say the killer advantages of OCaml for me are: Easy calling into C, and compiles to a native binary.
[1] http://libguestfs.org https://github.com/libguestfs/libguestfs
[2] I use the term "build system" in a rather narrow sense of something that (a) runs on Linux (b) lets the end user download a tarball and (c) builds using ./configure && make
> I'd also say the killer advantages of OCaml for me are: Easy calling into C, and compiles to a native binary.
Very true. And Ocamlbuild makes wonders.
[1]: http://ocaml.org/learn/companies.html
To answer one of your question. Yes, we have testing frameworks, both for unit[2] (inline[3]) testing and property testing[4]. As for the rest, it's not OCaml specific at all. :)
[2]: http://ounit.forge.ocamlcore.org/ [3]: https://github.com/vincent-hugot/iTeML [4]: https://github.com/c-cube/qcheck/
Yeah, you will notice that quotes that show you shouldn't take it too literally. ;) (also I have no doubt that Ocaml can work in a business. I just saw no one speak about it appart Jane Street, so I am curious)
What I was meaning that "a team with a hierarchical organization, not-ony-geniuses, and more than 4 people". Few of the serious Ocaml projects (if any) fall in this category, which is common for all businesses.
About the testing tooling, I am well aware of the state of these technologies. I even contributed to some of them. Sorry but it's light. It doesn't cover all the spectrum of what you'll want to test in a product.
Let's take your (excellent) libs and see:
- OUnit: unit tests
- QCheck: unit tests
- iTeML: unit tests
Where is integration and functional testing ? Unit Testing only test a very strict subset of the "does this work as intended" question. I recommend you to see how some projects do their testing. For example any classic Rails project. You could be surprised.
Some open questions I struggle myself to answer correctly:
- Mocking modules without having a build mess with oasis
- Managing the model in a sane way
- Integration testing is an horror
And many more that I can't recall now.
> Mocking modules without having a build mess with oasis
OCaml has parametrized modules (aka "functors"), which provide a very clean solution, compared to mocking whole modules anyway.
As a side note, I usually see mocking being used to test badly structured code. Refactoring that is usually a benefit for testing as well as a benefit for the code itself.
I am amazed to see how the MirageOS team used this, but this method cannot be used for all the code.
> As a side note, I usually see mocking being used to test badly structured code.
You could want to mock just because you are inspecting particular stack frames in a specific call stack, trying to reproduce a bug. It happens.
- QA: unit tests written using oUnit, and integration tests by using linked Docker containers.
- builds/releases: opam + packages written for Debian and Fedora based distributions. The packages provided on our website are built inside Docker just like all the other packages. Internal beta packages are uploaded to a separate volume/bucket and served via LibreS3 itself.
- Pain points: building a package compliant with packaging policy is more complicated because I usually need newer versions, or OCaml libraries that are not yet packaged. I planned to use opam to generate templates for LibreS3+dependencies but was waiting for the C backend to get packaged upstream first. My slides from last year list a few more problems that I encountered during development but they aren't an issue currently [4].
The main advantages of OCaml for me are: - the availability of high-level libraries that makes implementing HTTP APIs simpler (Ocsigen, Cryptokit, atdgen, etc.)
- event-driven/non-blocking architecture to support large number of concurrent users (Lwt)
- native binaries and static type system
- not having to look for certain bugs in my code like uninitialized variables, NULL dereferences, memory leaks or memory corruption bugs; which (used to) take up a significant amount of time when developing applications in C (although tooling on C side have improved with valgrind, clang -fsanitize= and Coverity).
That doesn't mean that code written in OCaml or the libraries that I use doesn't have bugs, I track them down and provide patches just as I would for a C library. Although perhaps writing code in OCaml has made me somewhat overconfident in my code, and I find bugs in other people's code more easier than in mine even if I'm not looking for them.
[1] http://gitweb.skylable.com/gitweb/?p=libres3.git;a=summary http://www.skylable.com/products/libres3
[2] http://gitweb.skylable.com/gitweb/?p=libres3.git;a=tree;f=li...
[3] http://www.skylable.com/products/sx
[4] https://ocaml.org/meetings/ocaml/2014/ocaml2014_13_slides.pd...
> Although perhaps writing code in OCaml has made me somewhat overconfident in my code
Many Ocamlers could be caught of overconfidence. It's hard to understate how ML typing help seeing the data flow.
First-class functions
C# has closures, and can pass function pointers with delegates.
--
immutability is the default
Mutability can be a useful tool in some situations, but it is often unnecessary and potentially harmful.
I think this point has probably been argued to death by now, but I still can't see this as a good idea. 'Side effects' are really just a reality of software development. For example if I want to fade in a GUI element on a page, I don't want to create a new element for every transition change. Theres nothing wrong with mutable state, but it is nice to have immutability for some use cases. In this case c# has valuetypes, and the const/readonly modifiers.
--
Strong static type checking
Yep C# has had this since the beginning. And you can even use 'dynamic' if you really want which moves all type checking to runtime.
--
Algebraic data types and pattern-matching
I feel like these are more buzz words than actual substance. The Html example given also doesn't seem exactly clear to me how the operations are being performed. Here's a pseudo c# way of doing it:
the typical object-oriented implementation of this in Java would involve multiple subclasses of a common base class.I assume they mean that all element types would inherit from a base IHtmlElement? This seems like a good thing to me.
--
Type inference
Again C# has these abilities with 'var a = 0' for example. It won't infer method return types, not because it couldn't, but because it's just generally clearer to have the explicit return type written there in the code for the reader.
--
a replay-capable debugger
C# has intelliTrace for this. Though I expect both suffer from the same problems, slow execution while in use, and huge memory footprint.
Um, both those concepts (algebraic data types and pattern matching) are very clearly defined, classic language features. Why would they be substanceless buzz words?
Here's a simple and oft-used example, which is option types. They correspond roughly to nulls so let's compare two blocks of pseudocode:
The two appear similar on the surface, but in the first example, we are relying on the programmer to check that myValue is not null. If he or she fails to check this, we'll likely throw an exception or worse. On the other hand in the second case, we do not need to trust anyone; instead we guarantee that there will be a non-null value added to the string, because it won't be possible to "extract" the inner value from myValue without entering a context in which myValue has a `Some` form. There is a lot more safety this way, since it's guaranteed by the language rather than the programmer. And not only is it more safe, but it's a lot clearer and more expressive as well. Consider a type with more than two variants, or holding multiple internal values rather than just one. The imperative if/then/else solution rapidly becomes verbose, obscure and error-prone, while the pattern matching case is just as straightforward as the above example.It might seem weird on the surface but after a few times using it it's the simplest thing in the world and you'll wish you had it in other languages. In fact, Rust, which isn't (primarily anyway) a functional programming language, makes heavy use of pattern matching.
This is not a type inference. It is a mere type propagation.
http://stackoverflow.com/questions/479883/how-good-is-the-c-...
If you are interesting in studying and comparing programming languages, I suggest picking up one of the five big functional languages: F#, OCaml, Haskell, Scala, or Clojure. Scala and Clojure are great, they run on the JVM, they perform well, are practical and pragmatic, but are probably not the best way to learn "functional" programming because they include too many additional features or leave out some features that some consider important. I would recommend starting with Haskell and then comparing it to the others to see if one of these could be helpful in your own programming. There are good, free resources for learning functional programming, see [1].
[1] http://learnyouahaskell.com
Mutability is one side effect. OCaml is not Haskell and doesn't have a type system baked in. Sure, sometimes you need to cheat, but mutability as the general rule is crazyland. Immutability gives you a compile-time assurance that your data structures are consistent and valid, as long as the tests in your constructor function are correct.
> Yep C# has had this since the beginning. And you can even use 'dynamic' if you really want which moves all type checking to runtime.
Well, it has strong static type checking... within the bounds of what its type system supports. Which is fairly limited.
> I feel like these are more buzz words than actual substance.
Others have responded to that, but in short, your impression is wrong.
> Again C# has these abilities with 'var a = 0' for example. It won't infer method return types, not because it couldn't, but because it's just generally clearer to have the explicit return type written there in the code for the reader.
C#'s "type inference" is cute, but you can't seriously compare it with OCaml's.
First-class functions in C# are nice, nearly as good as those in OCaml. The main annoyance is the incompatibility between Action<> and Func<> (and, in general, the fact that C# uses void rather than unit).
--
Immutability: for every mutable class in our code base, there are three or four immutable classes, and `readonly` is likely the most frequent keyword. Among these, we have an entire hierarchy of dozens of classes that are entirely immutable (and make heavy use of immutable containers from Microsoft) because it's still the easiest way to implement a multi-reader, single-writer thread-safe data structure. You can survive without immutable-by-default, you can survive without OCaml's
syntax, but it gets very, very annoying to work without in an object-oriented language.Algebraic data-types: I'm mostly writing compilers (yes, in C#, don't ask), and instead of writing a simple
I have to define a new class, three readonly fields, a constructor to set them, equality operators (R# helps, but still...), and five or six lines of visitor pattern boilerplate in order to get the "you missed a case" warning in pattern matching. This gets very annoying very quick, and there is no practical benefit to having all those classes here.--
Type inference: C# does manage to be fairly clever, although not as clever as OCaml. I have seldom found it to be lacking in general use, aside from the ability to define local functions (var f = a => { ... } does not work, you have to spell out the type of f). There have been a handful of times when I was trying to do something very clever and found myself limited by the type system, and had to write annotations myself.
And one time, I had to reimplement GADTs with generics and reflection. It was painful, but it works.
However, with a few minor tweaks (e.g. an option type), I would rather have the C# type inference than the OCaml one. The reason is that, if I want to do something very clever, I will not find myself limited to code that I can actually prove to the OCaml compiler as correct: I have, time and time again, resorted to reflection and code generation to work around such situations. In other words C#'s Obj.magic is a lot more powerful (and safe, and expressive) than OCaml's.
A fairly good example is Eliom's way of expressing the parameters of a service. In C# you would write in a PageController class
and have your web framework automatically bind this to POST /page/update/{id}?user={user} with the appropriate serialization for PageId and UserId. And writing such a framework is easy: a couple hundred lines of code, with run-time type safety.In OCaml you have to understand the entire Eliom_parameter framework: https://ocsigen.org/eliom/4.2/api/server/Eliom_parameter Just think of the mental firepower needed to create that framework in the first place!
More precisely, OCaml allows me to refactor my code very quickly, finding almost all mistakes even before any testsuite runs.
This is possible due to a combination of language features which only few languages have (notably, Haskell, SML and F#).
Not sure how this goes into total productivity, but this acceleration in development speed is something that no number of frameworks or libraries can match up.
I see this argument all the time. I don't know if the type of software development I do is special (I doubt it, it's fairly standard business software), but the amount of time I spent on business logic dominates total development time.
Past a certain fairly low threshold of core libraries (HTTP, JSON, CSV, database bindings, standard stuff like that), the amount of time libraries can save you is dwarfed by the cost of refactoring all that business logic.
On the other hand, if the language is so niche as to lack the basics, you can find your entire development budget spent on building functionality other languages offer out the box. I don't think OCaml is that niche...
It heavily depends on what you're doing. You might be surprised, but there is a lot of projects which do not rely on any 3rd party code and are built almost entirely from scratch.
There's no cost-benefit analysis here discussing the obvious pitfalls from a business perspective, such as a much reduced talent pool and probably higher salaries for the few programmers who are comfortable working with such an uncommon language. There's also no discussion about why they believe a business offering a calendar application needs to use such a technically demanding language. This would be more understandable for, say, Jane Street (high-frequency trading), but for this particular business my naive belief is that this probably will not be the "competitive advantage" they claim it is.
That might be true, but if they are competent in both, that's often because they have switched from OOP to FP and prefer the latter nowadays so if there is a selection between otherwise equivalent jobs, they would tend toward the FP job.
[1] https://github.com/libguestfs/libguestfs/tree/master/v2v
Ocaml was easier than even Java. Much less demanding than the minefield that is C++. And it certainly doesn't require more education. A different education, that's for sure, but not more.
As for requiring a stronger understanding of math… Languages like OCaml just don't pretend that programming is not math. So it tends to scare away those who fled math in the first place. C++ and Java are just as mathy (if not more, just see the sheer size of the specifications), but they don't really tell you.
<Grumpy Dijkstra> Programming should have been of the applied mathematics department, not the electronic engineering one. </Grumpy Dijkstra>
I have little experience in education, but my naive hypothesis is that it would be easier to teach a child, say, Python than any FP-oriented language. I suppose it goes without saying that if children were introduced to the lambda calculus beforehand this might not remain true.
See my comment beneath the root to see my clarification of the original post.
I don't think Dijkstra would disagree with me. After all, it was he who suggested the average American programmer would benefit "more from learning, say, Latin than yet another programming language."
It is also a form of resource management and incur resource management issues (do I need that value in this variable? Can I overwrite it and discard old value?).
The implicit state also is an implicit communication channel between different parts of program. In FP this communication is much more explicit, especially if you use something like Software Transactional Memory.
I think I listed enough points for you to reconsider your stance about Python being easier to grasp.
I know CMU does, however.
However, I'm puzzled about your remark about industry-oriented programs. I'm not sure what "industry-oriented programs" even means, but assuming you actually meant "FP doesn't have widespread use in the industry", I'd say that while this is true, a- it's becoming less true every day, through the use of mixed-paradigm languages, and b- even the purer FP languages see industry use.
"Right or wrong" this current reality you mentioned is shifting. Hybrid languages are becoming more common. Traditional languages are adopting functional idioms (and idioms from other paradigms as well!). It's not true that there is a clear-cut "natural" kind of programs for which to use FP; the area for which FP is good is pretty much good old "let's write software that works and has few bugs". If you have this goal, you can use FP to your advantage.
Any success story of someone using OCaml for this is welcome.
I've said nothing of depth about the merits of OCaml or FP in itself. I'm simply pointing out that the blog post clearly identifies the use of OCaml as a competitive advantage without discussing any of its drawbacks, whether these arise from the nature of the language itself or for historical reasons. This makes the article significantly less useful for a person trying to select a language for their company's codebase, which I take to be the whole point of writing the post to begin with.
Perhaps the real goal of the post was to attract the attention of OCaml developers, rather than discussing the titular question "Why we use OCaml." If this is the case, I'm sorry for having wasted my time criticizing it.
But then I realised that explaining why the listed features are good is hard. Someone who haven't been exposed to FP is going to take a lot of teaching and persuading. Or a damn good motivating example, but I have yet to find one.
Like a sibling comment said, this is an advocacy piece. I don't know if anyone will pick OCaml (or any language) for their company immediately after reading a blog, but it might pique their interest and make them research the language and consider it for their own projects, an interest which might eventually develop into considering the language for their day job. I know it was the case for me with Scala!
Really, this "oh no a foreign language" thing is way off. Every good software engineer I know would not bat an eye to learn a new language.
Ocaml is not quantum physics, it's pretty understandable language.
I'd also like to suggest that, for certain applications, the most touted benefit of functional programming (the ease of proving algorithms behave as intended) is not especially relevant. I would even dare to suggest that, in some cases, it may be more rational to hire a "decent" programmer than a "great" one.
This is to say nothing about the specific language features of OCaml or the utility of functional programming in general, just that the benefits need to be weighed against the costs. In an article claiming that using OCaml provides a "competitive advantage" from a business perspective, I would have expected a discussion of both.
Using a not very popular programming language can actually make it harder for you to hire people.
Mind giving an example?
> Using a not very popular programming language can actually make it harder for you to hire people.
Do you really want to work with people who can't learn a tiny little language?
> Mind giving an example?
You can use, for example, elaborate if-else constructs that firstly try to determine type of the value, then check for values.
I've never seen in imperative languages anything similar to conditional that checks both type and content of an expression, and allows to capture parts of that expression into variables. Well, except for precisely the pattern matching construct, where it was implemented deliberately after functional and declarative languages (Rust).
>> Using a not very popular programming language can actually make it harder for you to hire people.
> Do you really want to work with people who can't learn a tiny little language?
I see things a little differently. It's extremely difficult to find an idiot who can write OCaml, while it's fairly easy for C++, Java, or C#.
Also, allowing people to learn OCaml and use it every day on the job is a big plus for a company. It's way better than free sandwitches for breakfast in the long run.
That's nowhere near pattern matching. Two concepts make pattern matching vastly superior to if-else constructs:
- Non-exhaustive checks by the compiler.
- Destructuring
It's being a combination of destructuring, branch selection and variable assignment that makes pattern matching superior.
As for the hiring, it is a silly idea in general to seek "programmers in XXX language" (and to position yourself as an XXX programmer). Hire programmers. Just programmers. Languages are irrelevant.
That's dynamic typing. Very different from pattern matching over sum types: with dynamic typing, you can basically forget about most compile time guarantees. Seriously, you sound like, "why bother with Ocaml when JavaScript does the same thing?". I can tell you from first hand experience that they do not feel the same at all.
The other classic example, class hierarchies, is also very different from pattern matching —and much more cumbersome. The only language I know of that kinda bridged the two is Scala, with case classes.
No, that's run-time branch selection. Exactly as with pattern matching (`match .. with ..'), except the latter is more convenient.
> Seriously, you sound like, "why bother with Ocaml when JavaScript does the same thing?".
And you really think I consider elaborate constructions a better thing than concise pattern matching?
With pattern matching you'll get static checking if your matching is exhaustive. Impossible with an if ladder. Therefore they're not semantically equivalent.
Your solution with type and value checks in an if ladder was called "dynamic typing" exactly for this reason - you select paths dynamically in the runtime without any static checks on soundness of this selection.
I wouldn't. Pattern matching is primarly a conditional, that's why I focused on if-else.
> We're talking about language features semantically equivalent to pattern matching, and, turns out, there are none.
Why would you think that pattern matching revolves around compile-time guarantees? It does not. It's primarly a conditional, everything else is an optional, additional effect. You can have pattern matching in dynamically typed languages (Lisp, Erlang).
This is a very popular opinion, but I never see anyone taking the time to bring actual arguments. Do you have any experience hiring people or are you just spreading this seemingly self-evident claim?
If a language is popular, wouldn't more companies want to hire for it, which would make recruiting harder? See http://blog.activelylazy.co.uk/2013/05/27/choosing-a-program.... Also, being popular means that it is being known by a wider audience of people: isn't it harder to filter through all the applicants who say they can write in the popular language?
We also do javascript and, despite its popularity, we also get quite a few applicants who have it on their resume and have never used the language for anything useful.
Personally, my opinion is just hiring is hard. It is a very valid point though that if you are not willing to hire remote (which my company is not) you are very limited when trying to hire for less popular languages. Alternatively just train people, but that can be expensive.
That's a non-sequitur! The "unpopular languages make it harder to hire programmers" myth has been debunked many times right here on HN. Just hire smart people, not blub programmers.
Disclosure: I'm a functional programmer myself so, what do I know.
Some evidence this is just not true: http://gazagnaire.org/pub/SSGM10.pdf
The type of people who go about learning OCaml are arguably on the whole more competent and passionate than your average run of the mill Java programmer. Using OCaml might be a good way to separate the wheat from the chaff.
$ npm install install_ocaml -g
$ install_ocaml
https://ocaml.org/docs/install.html
I say "somewhat unique" because there are other languages in this space: Rust in particular may be the language of choice for these feature needs in the future.
High performance code in Haskell tends to rely on a combination of stream fusion, rewrite rules firing and judicious use of unboxing and bang patterns. I've heard it said that the OCaml compilers simplicity makes it far easier to know what code it's going to produce.
Disclaimer: I'm a Haskell user, so it might just be that I'm more familiar with Haskell's warts.
Over F#, OCaml provides a module system. This helps programming in the very large.
Over Haskell, OCaml provides a module system, and strict evaluation. The latter is a contended point, but proponents of OCaml claim it leads to memory/execution predictability. In Haskell (GHC) you often have to turn on optimizations in order to understand how your code will perform in reality, whereas byte-code interpreted OCaml acts just like natively compiled OCaml space wise, but is roughly 10 times faster in execution speed.
Wouldn't it change the require programming style to make use of the strict setting?
More info on strictness and Bang Patterns: https://wiki.haskell.org/Performance/Strictness#Explicit_str...
This was posted to r/haskell today and helps explain things a bit: http://blog.johantibell.com/2015/11/the-design-of-strict-has...
I think it only makes sense to talk about naive, idiomatic implementations of an algorithm when you're talking about the performance of a language. If you start talking about highly optimized versions of algorithms, it's harder to make statements about which language is faster. People who use numpy adeptly get extraordinary performance in Python, but I don't think that makes the argument that Python is faster than OCaml; it's hard to say what an equivalent level of optimization in OCaml would look like.
Every few years I take another look. Last time I researched this, I came across a bunch of incipient attempts at distributed, concurrent programming frameworks, but they were all abandoned around 2009. I couldn't find any mature, maintained frameworks, any easy-to-use concurrency primitives (lwt has, to my untrained eyes, a terrible API and is no match for goroutines or Erlang processes), and apparently the global interpreter lock is still there.
That's why I'm not, at this moment, using Rust or Nim in production. I want a mature toolset, a large, cohesive standard library, a large, active ecosystem, etc.
Go only recently reached this stage. Node.js has been there for some time. Elixir is there thanks to leveraging Erlang.