An OCaml manifesto – Problems and what's needed for widespread adoption
github.com
github.com
The situation improved drastically over the last years with opam which allows easy installation of most packages.
What's still missing, however is
- decent multi core
- resource management for things other than memory. For example, bigarrays don't cause enough gc pressure, and can therefore leak space. Thus people tend to manage that manually (acquire, release) which is even worse than what a C++ dev can use (unique_ptr)
- decent profiling (yes I can leverage strace and callgrind, but they don't understand lwt)
- decent runtime inspection. (what are all theses values in my heap?)
https://github.com/openvstorage/alba https://github.com/openvstorage/arakoon/
But what's interesting is that I was being upvoted until I've rephrased the comment. It used to be "That's an unfortunate comparison you've made". In the same time it sounded more moderate and more personal. I've edited it because moderation came at the cost of authenticity/honesty, and I thought addressing the comment risked automatic rejection of the underlying message on the OC's part. I did think that the actual sentiment could sound worse to others, but it would be hypocritical of me not to have gone with it. The underlying message is precisely "don't conform on account of genuineness".
Hope this cleared something up after all
In particular, while the one used in RWO (Core) seems very nice, it looks like its use of named parameters is via some kind of syntax extension (edit: not the case; see LeonidasXIV's reply). This seems to be a pretty large difference in approach, and while I can appreciate the reasons behind using named parameters in Core, it makes me worried about writing code that might have to interact across different stdlibs.
As such, I've decided to hold off on learning OCaml for now until resolutions to these community issues can be made more clear. But perhaps I just prefer the Python approach, and that may not be the same objective as those in OCaml.
Edit: Actually, the nail in the coffin was probably when I found a web framework that I wanted to use, but then the author explained why he was removing support for both of the async frameworks and going with only one of them (the one that RWO doesn't use). And then the other main web framework I found also used non-RWO async... :(
No worries, OCaml natively supports named (and optional) parameters. What Core does, though, is reshuffle many function signatures to be more useful when using those parameters.
I suppose you're talking about this[0] blog post, his reasons are pretty clear: hardly anybody uses Async apart from Jane Street, whereas Lwt is much more popular, used by standalone projects, many bindings, as well as Mirage.
The reason RWO used Async is that it is basically a companion to Core and one of the authors of the book is also the author of both Core and Async.
The problem with Async is that what seems to be the best resource for learning OCaml in 2016 teaches Async, not Lwt. I was excited to use it until I discovered that many things I was interested in use Lwt instead. It's not hard to learn Lwt, but this discovery made me think about the problems I've had as a professional Perl programmer in dealing with Perl 5's multiple competing event loops.
The upside is that with the hopefully soon upcoming algebraic effects that are supposed to appear in OCaml sooner or later, Lwt and Async might end up being obsoleted or subsumed into a compatible set of Async primitives.
I'd rather have a system to generate composable queries (hopefully in a typesafe manner) and able to use modern SQL syntax (eg, window functions), without the stuff you typically find in an ORM (identity map, magic setters, etc)
Technically there is SQLProvider, but it's ugly as sin (I also don't care for type providers in general, I don't need automatic code generation, I have no problem specifying my schema and not needing access to a database server just to compile my code).
Edit: Forgot to mention that no Visual Studio or Windows is used in the process.
Type safe solutions, in terms of using code expressions that get translated into SQL, exist too. However everywhere I look people complain about how horribly inefficient and frequently buggy the generated SQL is. This is true in LINQ, EF, Scala's SQL generator, etc. Stackoverflow obviously created Dapper for a reason. So I've thus far avoided these "too clever by half" solutions; Dapper is perfectly fine.
Yes they lack composability and type safety. You could easily add composibility in the guise of something line various existing JsonQL libraries. Type safety, at some point (crossing process/network boundaries) you just have to be pragmatic and let go.
I still hold out hope for a datalog-to-sql layer, someday.
i love ocaml, i love ml languages, and i think ocaml is a very good ml
but, i think its kinda too late for it, i think people who want to program with types today most likely will go to rust, scala or f#
ocaml is very niche
[0] The paper gives set-union as an example.
[1] https://www.youtube.com/watch?v=hIZxTQP1ifo . (Sorry for throwing video at you -- there's probably a write-up somewhere, but I'm in a bit of a rush, and this was the thing that came to mind.)
Now we just need a Haskell compiler which actually bothers to provide the "pretty essential for sanity" for global uniqueness. Currently, I count zero compilers in common use which do.
(EDIT: Just to forestall the inevitable: Yes, there are some weird and dangerous extensions you can enable, but mostly it's pretty clear that they're dangerous. It doesn't seem to be a problem in practice.)
Because they never bothered to actually implement the checks to guarantee global uniqueness in GHC?
> (EDIT: Just to forestall the inevitable: Yes, there are some weird and dangerous extensions you can enable, but mostly it's pretty clear that they're dangerous. It doesn't seem to be a problem in practice.)
No weird and dangerous extensions in use. Vanilla Haskell.
Sure they did. Orphan instances are detected by the compiler.
Orphan warnings tell you that you defined an instance in the wrong place.
Things you don't receive warnings for:
- Defining the same instance twice.
- Having code which uses both instances.
Those two should be hard compiler errors, because they break data structures at runtime and make code behave incorrectly.Sorry, orphan warnings are multiple magnitudes of escalation levels away from what should happen and are focused on something different (code hygiene vs. THIS-CODE-IS-WRONG-AND-BROKEN-AND-WILL-CORRUPT-SHIT-AT-RUNTIME-I-WILL-FAIL-TO-COMPILE-THIS).
I believe I've understood the issue, but if I've misunderstood then I feel extra need to comment, that my misunderstanding might be clarified.
From my first reading of what you'd written, tome seemed spot on.
On careful re-examination (after reading this comment), I think when you said "Things you don't receive warnings for:" you did not mean to say that you can write that code and receive no warnings (a reasonable interpretation), but that the warnings would not read as directed at the points you raise (also a reasonable interpretation) - which (you note) are substantially more important than "this is generally a good idea."
I claim that if you never have orphan instance warnings then your code has "the guarantees Kmett keeps boasting about". In particular you can never "define the same instance twice", and this of course implies that you cannot "have code which uses both instances".
What exactly is your complaint and why does my claim not address it?
In haskell you can just say `show a` as long as `a` is of a type that implements the `Show` typeclass, but OCaml currently has no good answer to this. You end up with `Int.to_string a` or what-have-you. It isn't less powerful, technically, just more verbose.
Modular implicits allow you to define a function `show : (S : sig type t show : t -> string end) -> S.t -> string` and then mark the first argument implicit, so you can call `show a` and the compiler fills in the first argument with the appropriate module for the type of `a`.
There are proofs that this particular feature of typeclasses is impossible to put into OCaml's module system (something to do with functors and type aliasing and not being able to ensure that there is a unique `Show` instance per type), but modular implicits come close.
I think the biggest issue with OCaml for mainstream adoption is the syntax, honestly.
Didn't think of it that way. Well-put and I totally agree. So odd that one hits that sweet spot in design yet gets almost no uptake. Whatever problems like OP posted need to get solved because interesting and useful things are sure to come from mainstream attention to it. Useful even outside Ocaml given its influences on other languages.
Another thing is the compiler. It's reportedly easy to understand and robust compared to most. I recall reading Esterel's report on DO-178B certifying their code generator written in Ocaml. They had to do source-to-object-code equivalence. They said that, very unusually, the Ocaml toolchain was well-structured to the point they only need minor tweaks to get the job done.
So, great language design plus great implementation equals great opportunity for robust, app development. The concurrency situation is ridiculous, though. Even C-like languages and Haskell have safe concurrency techniques. They need to get on that shit cuz nobody uses single-core boxes anymore unless they're a small shop licensing Oracle. ;)
Indeed, a barrier in itself, after working through RWO I was impressed with the language, clearly very powerful, but some aspects of the language are syntactically unpleasant.
Yes, yes, semantics, not syntax, but new comers will come in all forms, including those who will just say no based on the general "look" of the language. IMO, if OCaml had the same power but looked more like SML it would have much greater adoption.
Anyway, modular implicits and multi-core will certainly help OCaml to grain traction, syntax notwithstanding.
Scala "implicits" mean two different things:
1) implicit conversions. e.g. If there is an implicit def f(x: Int): String = x.toString in scope, then any time you try to use an Int where a String is expected, Int::toString will be called automatically. Using implicit conversions like this is generally frowned upon and also where the "please no" probably comes from.
implicit conversions are used frequently but almost entirely for extension methods (there's even sugar for it now: "implicit class")
2) implicit parameters. These are parameters in a functions type signature keyed by type. You can emulate Haskell typeclasses (sans canonicity) with these. For instance
def monoidSum[A](as: List[A])(implicit m: Monoid[A]): A = as.foldLeft(m.zero)(m.append)
is equivalent to the Haskell
monoidSum :: (Monoid a) => [a] -> a
monoidSum as = foldl' mappend mempty
Scala implicit parameters can be defined inductively and depend on other implicit parameters. You can do this to perform some really cool type-level computation. The shapeless library is full of stuff like this. Once you understand implicit parameters, it really is somewhat nice to write functions that operate on arbitrarily-sized HLists and Coproducts.
Modular implicits are similar to (and inspired by?) Scala implicit parameters.
The closest I've been able to get is Oasis+OPAM, but it's just not the same.
I don't care about the minor issues, but about the major ones; the ambiguities concerning ;, if and match; essentially, as soon as you're using imperative features (which often result in using ;), you have to be very careful to make sure your code means what you think it means.
I'm a Scala dev. I'm playing with Rust for lower level stuff, don't know much about F#, but FWIW I would rather code in Ocaml than Scala.
At my previous job I tried to introduce some functional languages without much success. The stack was C++/Java and a lot of Python. I learned Scala because it runs on the JVM and I thought that may help.
Later I moved to another city and when looking at job offers I saw a few Scala offers (and no Ocaml in sight) so I settled on Scala.
IMHO Scala gained traction because it runs on the JVM. It's easier to train Java devs because you can start by writing Java-in-Scala and incorporate specific features as you go. The web ecosystem is nice (at work we're using Play.) There's also a few killer apps, like Spark.
I'm currently trying to become as proficient in Ocaml as I am with Scala, but I'm already working with Scala 8+ hours a day so it's not that easy.
The key difference for me is "is there a polished development environment where I can get from zero to debugging within minutes".
From my perspective Mirage is OCaml's 'killer app' (I'm sure others would disagree). Where I could see it excelling is as an open-source home server platform. I'm not sure what the current progress is but I know some of the Mirage developers were working on this particular use case, I'll see if I can find the link.
Could something like Mirage exist for F#? It could, though the Mirage team have a considerable head start.
EDIT: The project I was referring to before is Nymote: http://nymote.org/
Haskell has higher-kinded type parameters and Idris has dependent types. But SML and OCaml don't even have typeclasses at all, much less higher kinds.
Furthermore, what makes Rust's type system interesting isn't just its use of regions (which the ML Kit used); it's regions combined with inherited mutability plus the borrow check to maintain soundness.
OCaml has a powerful module system, labeled and optional function parameters, row polymorphism, equirecursive types, and GADTs. Most of these features would be fairly difficult to add to Rust.
OCaml also has a better track record with soundness issues than Rust, and more interesting fragments of the language have been formalized.
There is a fun library[0] with a pretty good paper[1] on how to do that in OCaml :-)
[0]: https://github.com/ocamllabs/higher
[1]: https://ocamllabs.github.io/higher/lightweight-higher-kinded...
GHC 8.0 almost has dependent types and the expectation is feature completeness in 8.2, thought what's in 8.0 is already useful.
I like and use Rust and the design decisions that led to a very capable region system that allows one to write data race free and memory correct code is excellent. Haskell doesn't have such a thing because it doesn't try avoid GC.
I didn't do a point by point comparison of Rust expressiveness vs Haskell recently, but I don't remember it being close. Rafael might need to write a Rust for for Haskellers as an update of his "for OCamlers" post a while back.
It is a bit outdated but as far as I can see not inaccurate yet. I only plan to update it when HKTs make it to Rust.
... is this what you mean?
I revere Harper, but his anti-Haskell rants just seem unhinged to me.
I've heard bad things about Harper, but then he's an accomplished fellow, which doesn't mean he has to be nice. I only expect him to be truthful, is all. Maybe he's just ML's LISP zealot like the now deceased Dane whose name I forgot.
1. Really good unicode support
2. A dead simple high quality package manager i.e. bundler, mix, cargo.
I'd also add built-in concurrency primitives i.e. (Channels in Go, Processes in Erlang), though this probably isn't a must have currently, but will surely only become more important as time progresses into the multicore future.
Yes, but I just think unicode support is so pervasive its not something you should really leave up to a library in 2016. Still this is just my biased opinion as a developer who needs it on virtually every project.
"2. opam."
see: https://opam.ocaml.org/doc/Packaging.html#Publishing, - basically, I think requiring a PR on github to get a package published is not "dead simple".
Finally, the title contains "widespread adoption". There is no modern language on the Tiobe Index top 20 without those two characteristics except for possibly MATLAB and C# which both don't have good package managers as far as I know, but I could be wrong since I've never used either one of them.
The List: (http://www.tiobe.com/tiobe_index)
Anyway, I'd like to see ML get more adoption. I think fixing these things are more fundamentally important than some others such as web frameworks, etc...
On #2, I disagree. The effort required to make the pull request is a small fraction of the effort required to make a library worth adding to Opam. At the same time, it's enough effort that it should prevent the NPM problem of a million stupid "libraries" for 2 line functions.
I'm in the same boat as a lot of people here, in that I think OCaml looks great except for a few big shortcomings that they've ignored for a long time (unicode, multi-threading, etc.) that end up being deal killers for me. As I put it in another thread a while back, it's not even that I need the missing functionality all the time, it's that they refuse to add any of it for as long as possible.
According to the author of one of the Unicode libraries that is the only thing the compiler should distribute by default, and I agree with that, otherwise you need everything that comes with Unicode too to make it useful: normalization, segmentation, etc. If an application needs that it can link with the unicode library, having it separate from the compiler means features and updates can be implemented faster.
Also see here on how other languages (Python in this example) got it wrong: https://sympa.inria.fr/sympa/arc/caml-list/2014-07/msg00050.... https://sympa.inria.fr/sympa/arc/caml-list/2014-07/msg00054....
The linked introduction to Unicode (for OCaml but not only) is also very informative: http://erratique.ch/software/uucp/doc/Uucp.html#uminimal
It got a lot of attention on Tim Bray's widefinder benchmark:
https://www.tbray.org/ongoing/When/200x/2007/10/30/WF-Result...
Overall I'd say that OCaml is more pragmatic.
Logging is another popular example of a "benign" effect, but I personally don't think is: I mean, your logging library could be doing all kinds of weird things like shipping logs over TCP to a remote syslog daemon... and I don't want that to be hidden under the rug (using unsafePerformIO or similar) only to blow up later when the network happens to be down.
Anyway, having programmed in O'Caml for 3-4 years, I was won over to Haskell by the compiler-supported (side) effect tracking. I think it's also important to note the distinction between side effects and plain (desired) effects. Not enough people do this in these sorts of discussions.
I am on the fence with ORMs. Yes, they make 80% of cases possible, remaining 19% difficult and last 1% sometime impossible.
Having Slick (Scala) and JOOQ (Java) makes code much more transparent, explicit and easy to maintain. Debugging nasty stateful session bugs (oh JPA) can be nightmare.
I, certainly, do not remember one for JS or Node.