Functional Programming in OCaml
cs.cornell.edu
cs.cornell.edu
I stroll through the internet aimlessly and I find a page that mentions OCaml and my eyes light up; I see an .ml file and savor how it looks - functions written with great economy of expression, pattern matching through which data flows in well-defined pathways, abstract data types forming a network of rugged pipes and girders that makes invalid states impossible; and .mli files that presents modules like classic prose -- no hedges, contortions, or obscurities that undercut the reader's ability to understand the flow of data and the pathways through which it flows.
"Expression, carrying the elegance that comes from compressed energy, is like a perfectly tuned stringed instrument: the strings are taut to exact degrees to correspond to exact pitches that stand in exact relations to one another. The pitches and their relations exist before the strings are tuned. Each string is tuned to a pitch and the results are judged by comparison to this pre-existing reality. A musician tuning an instrument is not finished until everything is exactly right, but once it is exactly right, there is simply nothing left to do." -- Clear and Simple as the Truth: Writing Classic Prose, Thomas, Turner.
Unfortunately, I don’t know what to build with it... I’m the kind of person that builds projects for professional advancement, which curtails my creativity. I’ve thought of making a simple web server. Or maybe learning ReasonML. Any suggestions welcome.
So do you think it would be a good choice for creating a make-like tool, in which you want to support doing transitive / hierarchical makes, like target A depends on B, and B on C, so if C is newer than A, the tool should build B from C and then A from B, and other such variations?
Working on such an idea, currently in Python, and have an interest in and am reading about OCaml, hence asking. I think the work may involve a topological sort.
- The Dune build system for OCaml, also written in OCaml, does exactly what you are describing: https://dune.build/
- Jane Street's Incremental library lets you build auto-updating computations which change whenever their inputs change: https://github.com/janestreet/incremental
Honourable mention: Thomas Leonard's really interesting series where he describes rewriting his dependency management tool, 0install, from Python to OCaml http://roscidus.com/blog/blog/2014/06/06/python-to-ocaml-ret...
The Jane Street Incremental library idea sounds cool. I had thought of something very roughly like it, but for memory data, recently, in connection with that tool I'm working on.
Had come across Leonard's series of moving 0install from Python to OCaml earlier. Need to revisit it.
And you can interop between reason and Javascript!
When I was 20, I thought Ocaml was the best thing in the world, because I was writing it. Now, after years without using it, I have to maintain other people's code, my opinion changed.
I had not seen your project before and this looks exactly like the right hammer for a problem I needed to hit a week or so go.
I recently came back to OCaml (and js_of_ocaml) after being away for a few years. In the time I was gone, js_of_ocaml moved from camlp4 to ppx.
(edited to fix link)
I’ve evaluated it for a lot of smaller stuff I’ve needed to get done, but ultimately it just ends up being easier to use a framework or language that I already know.
That's exactly my experience with F# (not surprising, knowing how similar OCaml and F# are), but until I read your comment I didn't know how to explain this joy of reading and writing code in it.
Yup! The “official” notepads of my university are nice quality paper. They cost more but I enjoy them so much that I buy them anyhow.
Conversely, I’ve also had notepads with paper that felt so horrible to write on that I were unable to use them. Fortunately a friend of mine uses ink instead of mechanical pencil so for her using those notepads was not a problem so I gave them to her rather than having them sit in a closet or having to hand them over to recycling unused.
https://www.wilko.com/en-uk/wilko-graph-pad-a4-50-sheets-80g...
They are ridiculously cheap but the paper is lovely and they work fantastic with my ballpoints of choice.
I buy them 16 at a time.
I learned to program in Caml (20 years ago now... it still seems like yesterday) but switched to curly brackets land when I started working, and more recently Python. Last month I started reading Learn You a Haskell for Great Good and that made me nostalgic for FP. Haskell’s syntax and strict management of side effects is a bit of a turn-off though. The article and the comments have firmly put me in the Caml camp, but now I have to figure out which version (OCaml, F#, ReasonML) to go for.
Advice or opinions on the topic are welcome!
If you want to write a command-line application, you can choose either OCaml or Reason, depending on your taste of syntax - both compile to native statically linked binaries. If you're doing anything related to compilers, OCaml is particularly suited for that, and you'll be in very good hands with Menhir (see: https://pl-rants.net/posts/case-for-new-lang/). Another systems software written in OCaml is unison (https://github.com/bcpierce00/unison), which is a file synchronization tool with a clear specification.
But if you want to write database backed web application servers in OCaml, with sessions, background jobs, admin interfaces, ORMs, database migrations and all that jazz, then you might want to give F# a go, or Haskell which has a better story around it. I do have a tiny Node server written in Reason that I use as an API endpoint but if I were to build a full-fledged system, there is nothing yet in the OCaml world like Rails or Django.
An added benefit would be to use this new language at work, which means that the size of the ecosystem (3rd-party modules, frameworks, etc.) can be a factor - one in which I guess OCaml is behind F# and Reason?
it has nice error messages and clean syntax. it is easily installable on any platform. i took dan grossman’s programming language course on coursera. the first part of the course uses sml, and i simultaneously used sml, f#, racket, and scala to do the homework in. f# had the shortest solutions by far if counting lines of code.
a unique feature that i really like is its active patterns. f# already has amazing pattern matching, but you can define your own pattern matching with active patterns very easily. it is sort of close to defining your own pattern matching dsl.
in my opinion, it is criminally underused and under marketed by microsoft. in a world where python is taking over, f# is very refreshing.
> in a world where python is taking over, f# is very refreshing.
Can you elaborate? I happen to like Python but I'm always open for refreshments :D
as for books, there are a lot. none of them really fully resonate with me, although they are usually pretty good, but the book functional programming using f# by hansen and rischel is fairly nice. it is more academic in tone, although it was written a while ago against an older version of f#, so there may be some slightly out of date or style code. the book expert f# is by the language designer, and it shows some nice advanced stuff in a clear way.
i also recommend going through the little mler by translating the sml code in the book to f#. it will get you used to using types and pattern matching the way they are done in ml dialects. you will be able to do everything up until they cover the module system.
i have some very simple code here: https://github.com/nikofeyn/ray-tracing-with-fsharp/tree/mas...
it isn't documented or to my usual style, as it is an incomplete project. i was going through the book ray tracing in one weekend by peter shirley and didn't finish. and this code was actually "ported" from racket code that i had originally started the book with. so it went from c++ -> racket -> f#. however, it shows some representative f# syntax for some numerical stuff. i have some more f# projects that show more type-based programming and pattern matching styles, but they are in private repositories.
i personally just do not like python. it feels very sloppily designed to me, and i prefer scheme-based languages like racket and ml-based languages like f#. i have only worked in python when work has compelled me to, and i hated every instance of it. a large majority of it is that python is just difficult to reason about and is sloppy. that and the ecosystem, while vast, is very sloppy. i have encountered countless dependency issues and version headaches with python. it just isn't fun. all the work to get something working in python is just not worth it and unless you are reliant on some particular library, i personally don't see a reason to use it.
meanwhile, racket and f# remain as easy to develop code as python, except that the code developed works and works well. they are just extremely nice languages to use. they are the only languages i have used where it doesn't seem they are fighting me.
if you use python's async code, check out f#'s async expressions. they are extremely easy to use.
i highly recommend dan grossman's programming languages course on coursera. the first module uses sml (you can basically directly port all the code and your own code to f# for fun and very easily). the second module uses racket. and the third module uses ruby. i recommend it for any programmer and especially for some not used to the functional style.
Building real things is great, but honestly for me one of the most satisfying things is just programming in an expressive and clean way for its own sake. It's great when you get a language or a tool that faciliates that.
> Milner received the Turing Award in 1991 in large part for his work on ML. The award citation includes this praise: "ML was way ahead of its time. It is built on clean and well-articulated mathematical ideas, teased apart so that they can be studied independently and relatively easily remixed and reused. ML has influenced many practical languages, including Java, Scala, and Microsoft's F#. Indeed, no serious language designer should ignore this example of good design."
What does 'award citation' mean in this context? Surely it can't mean the blurb around the original award back in '91.
It appears to be part of a biography that one can find for every Turing award winner. It's unclear when these blurbs were written or for what specific purpose.
- A cross-platform native-compiling GUI library with a React-influenced design: https://github.com/revery-ui/revery
- A lightning-fast Node version manager: https://github.com/Schniz/fnm
- Erlang-like multicore actors: https://github.com/ostera/reactor
That said, it's early days and the ecosystem has to (and will) mature a lot.
Is the implication that plain OCaml is not good?
The esy package manager alone is a huge improvement over opam and npm.
All that said, the OCaml tooling is definitely making strides in the right direction. Dune for one thing is incredible and we are lucky to have it. I'm just saying there is room to make things easier for developers, and Reason is also moving that forward.
- Having to learn how to use let ... in
- Having to learn how to use semi-colons and where and why
- Having to enclose `else` clauses in parentheses or begin/end keywords and learning why they are necessary
- Having to use multiple pairs of parentheses to enclose function arguments like `foo (a + b) (c - d)`. With Reason it's `foo(a + b, c - d)`. And another bonus, function application in Reason actually looks like high school math function application
- Having to learn to distinguish between tuples and multiple arguments in data constructors
Those are just off the top of my head. In addition, ReasonML's refmt tool lets you avoid bikeshedding discussions about code formatting–it's a whole-program formatter. And some additional syntax niceties like single-@-symbol annotations and the upcoming single-line comments (//)–something OCaml will most likely never get.
Especially with nested expressions, the inability to find mismatched parens and braces ultimately turned my team's experiment with it sour.
The base language's grammar is said to have inconsistencies, but I think Reason's 1:1 matching while keeping older constructs makes translating very difficult. There is a tool to do so, but it's lossy.
That said, I use Reason React for a small front end project at work and sort of wish that the ppx for JSX wasn't tied to Reason, so that vanilla OCaml could be used instead.
An example of lossy translation from Reason -> OCaml:
https://reasonml.github.io/en/try?rrjsx=true&reason=DYUwLgBA...
Doesn't it allow to quickly code something like HyperScript-Helpers?
the explicit_arity being added in. I can't think of other examples at the moment.
Maybe lossy is the wrong word, but the transforms historically had gotchas. PPX and what not. Especially when converting between Reason syntax version upgrades and the more stable OCaml syntax (locked to OCaml 4.2 because of bucklescript).
That doesn't necessarily mean lossy, but just that knowing what refmt will error out on depending on the version of Reason the code was written to target vs. locked down ML.
> You might think this course is about OCaml. It's not.
> This course is about making you a better programmer.
What I like about it from a learning perspective is that it makes a transition into functional programming much simpler than it would be with Haskell, where you have to completely change the way you program from the very beginning.
https://github.com/ocramz/xeno/blob/master/src/Xeno/DOM.hs
I would be interested in an analysis of this code from anyone who actually does know Haskell in case I'm misreading it.
begin ... end
It's ugly and stands out in its awkwardness, almost as if by design, with the compiler saying: "I will let you do this, but do think about it some more and try and come up with something more elegant."
Probably anyone who's interested here knows, but it may be appropriate to point out that you are using the word 'strict' in two different senses, first in the informal sense of being restrictive, and second in the formal sense of strict evaluation (which is why it makes sense to say that OCaml is less strict, and yet strict by default).
Disclaimer: I've written far more OCaml code than Haskell, so maybe my opinion may be biased.
Good idea to learn both, anyway :-P
Edit: compilation speed too, huge difference
I think where ReasonML/OCaml wins hands-down is its compiler. Bucklescript (and I've heard similar things about other compilers, but can't vouch for it) is lightning quick, and the community has gone leaps and bounds to make it super easy to bootstrap (including with create-react-app). GHC can do amazing things but it's not the fastest out there, and the compiler for the JS backend is abysmally slow. The code Bucklescript produces is very high quality and performant. The library ecosystem is reasonably strong and I expect (but haven't done much testing) that because of the strictness and relative simplicity of the language, library code would tend to be pretty performant and easy to use. I've never found myself in OCaml wondering what string type I should use, or attempting to parse some absurdly complex type hierarchy in a library.
I think for a frontend project, to most programmers I would recommend ReasonML. For one thing, I think even most without experience with FP would find ReasonML to be a giant improvement over Flow (which infuriates me as a type checker), and it doesn't take a ton of time to learn. ReasonReact is a pretty sweet library and I prefer it to React even setting aside language choice. Compared to Haskell, its strictness means it doesn't require a special runtime, generate slow/impenetrable code, or hog tons of resources -- all things that are issues in frontend Haskell. For those who really want to use Haskell on the frontend I'd recommend PureScript instead (also you get the bonus of a kickass record system).
For backend, I think OCaml will get you up and running sooner, but in the long run you're going to find yourself missing the type system and language features Haskell has. GHC's speed on the backend is tolerable (especially if you use nix), and I think Haskell is competitive in performance to OCaml (perhaps better with optimization). Most importantly, I think Haskell is second to none when it comes to correct implementations of business types and logic, creating powerful and reliable abstractions, and eliminating boilerplate, and I think that forms a greater advantage in backend code.
EDIT: this turned into quite the essay...
Our lucky day :)
Was there a reason you didn't go the Haskell -> Js route? Less mature but benefits of keeping the same language would have trumped the downsides? Besides compilation speed you mentioned...
I'd love to hear more on the compile to Js scene from someone experienced like you. Do you have a 'weblog'?
Edit: Have you tried Scala.js by any chance?
Let GHC do what it does best; run it on the server.
Scala.js is amazing, but it lacks lazy loading mechanism popular in TypeScript/JavaScript ecosystem -- basically you can't create a set of "tiny" modules (since the Scala compiler + standard lib are integral to each exported module), so you wind up creating a single "app.js" that contains the world (or perhaps 2 modules, one for the frontend and another for the backend).
It works but it's not ideal -- would be amazing to be able to mixin a standalone scala compiler + standard lib module that lazy loaded modules could depend on.
Pardon me for wading through your history but you seem to have used Buckle script too- and claim that it has insane compilation speed and tiny Js size - (which would translate to a one time perf gain in download/parse/exec). How is the performance difference over the course of application? Would you still use Scala.Js if you didn't use Scala on the back end?
Also, why not create multiple scala-Js projects to do what you're looking to do? You'll have to fight SBT but if you die you're a martyr and if you survive you get to fight it another day...
That won't help. Scala.js banks heavily on whole program optimization to reduce the size of emitted Javascript. Two independent Scala.js modules will contain redundant code including likely upwards of 100kb of Scala standard library (collections, futures, etc.)
There is an issue about webpack-style code splitting support for Scala.js somewhere on github, and Sebastien said it's doable with the current architecture, so we'll likely get that eventually, but it's not there yet.
I do love working with Scala.js - great language and great interop with JS.
I touched on this a bit, but the main thing is the compiler experience. I've written apps in Haskell compiled to JS before; specifically I was using Miso. While I very much enjoy writing Haskell -- that part was great -- the compiler is very slow, the binary is huge, and the generated code is completely incomprehensible. There are a few other disadvantages, such as runtime bulk, speed, laziness and the multitude of string types as well. Bucklescript is _so_ much faster than ghcjs and the tooling fits neatly into an established workflow (i.e. webpack, create-react-app etc). It compiles almost as fast as ES6, it has a built in code formatter, it supports JSX natively, etc.
That, plus I had been curious about trying OCaml for a while and I figured why not.
I think overall it needs more development, in particular in regards to UX and integrating into a modern development workflow. I'm excited to see its progress :)
Have you ever considered js_of_ocaml (https://github.com/ocsigen/js_of_ocaml)? What were your reasons for choosing ReasonML?
Could you elaborate? Ocaml has monads? Do you mean convenient do-notation for monads, or something else?
In general, I've found programming with monads in Ocaml far less pleasant than Haskell, owing to the lack of overloading. The module implementation of monads requires you to functorise your code and apply a functor for every monad you want, while in Haskell I just (>>=) and the type system figures out what I meant. The verbosity makes me want to avoid them entirely.
The only other popular functional programming language I've seen which has a proper zoo of monads is Scala, which, I think by no coincidence, supports implicits and supports higher-kinding without having to functorise.
No, OCaml really isn't a language where things move forward; quite the opposite.
Also, if you had actually written any Haskell code you'd know it's not about eliminating any side effects, it's about denoting them in the type system. Your comment reeks of someone who hasn't actually used either of these languages.
[1] http://keleshev.com/composable-error-handling-in-ocaml
[2] https://victor.darvariu.me/jekyll/update/2018/06/19/ppx-tuto...
From a nontechnical perspective, some changes in the Haskell community and ecosystem also turned me off on it starting around 2011, and I find the ocaml one to be a bit more up my alley.
This course is for teaching students who know how to program but are new to functional paradigm. The way I understand the above is that OCaml is impure, so it's easier to have some parts in imperative style to get unstuck and move forward at first. That may reduce frustration for some students. Then over time, as the functional approach sinks in, you move to a purer style (with sometimes imperative parts, but well encapsulated). Haskell would be more like "let's jump at the deep end of the functional pool" --- which can work for some too.
http://www.cs.cornell.edu/courses/cs3110/2019sp/textbook/bas...
Static functional ... lisp?
> F# is a member of the ML language family and originated as a .NET Framework implementation of a core of the programming language OCaml
So F# is based on OCaml and is very similar.
Put another way, OCaml only works on Windows via MinGW if I recall correctly. While that isn't a problem for most folks, I prefer to steer clear and only use a language on a platform with a significant number of users. Python is used extensively on Windows, Mac, Linux, ARM...etc, so I'll use it on any of those machines. I would guess nearly all OCaml use is done via Linux and next to zero on Windows, so I avoid it on Windows (not worth the effort). F# on the other hand has some support from Microsoft (not a ton, but enough) and a decent amount from the community with some neat innovations (at least I think they are) like type providers. It has full .NET interop when you need it.
Most of the syntax and method of using pattern matching everywhere is the same between them. The difference with F# that I find annoying is that I'm not super familiar with C#, so the times when you need to call .NET (Ex: iterating through a directory) directly, it is super obvious for someone with C# experience, but involves lots of digging through horrid Microsoft doc for me where only C# and VB.NET is covered 80% (I just made up that number, but F# is the least documented of the three official Lang's) of the time.
Afaik OCaml could be built with MSVC. Lexifi use OCaml on windows only and even have a .NET layer
"FWIW if you're interested I'd also like to mention the huge impact OCaml had on the design of .NET generics and C# 2.0, which I've never properly described on this list. It was seeing and experiencing polymorphic programming in OCaml and SML that made us persevere with the long and torturous process of convincing Microsoft that they should add such an "experimental and academic" feature as generics to their flagship runtime and languages. Of course we were in reality just making 1970s ideas work in practice, but at least now even Visual Basic has generics."
I’m thinking hard about picking up a new language and I think it’ll be oCaml, but F# does have the .net ecosystem behind it... how is ocaml’s stdlib? Is it easy to find what you need in 3rd-party modules?
That said, I don’t tend to use a huge number of 3rd-party modules, so it’s not a big differentiator for me. Too much of a liability to have dependencies on some rando from the internet: for work, we tend to stick to modules from trusted sources in the rare chance we use them (eg, Jane street).
I've learned from the ground up (since college) in a functional style through HtDP [1] and have used Racket (not just the teaching languages) and OCaml a good deal at this point. Some notes on my experiences with functional languages:
OCaml
The Good:
- Incredibly powerful data matching/construction
- Typing gives good runtime guarantees
The Bad:
- The actual type checking messages from the compiler aren't very fun to wrangle with
- Parens being optional but then sometimes being needed for data constructions adds to confusion along with types
- There are a lot of other quirks that I only see occasionally but pull me out of ever thinking "this is the language I want to code in mainly". Things like being required to define mutually recursive functions in the same define clause, chained together with "and". The ambiguity of the need for semicolons, similar to parens. I won't detail them all here, but the quirks sum to something just large enough to be quite annoying to use.
Summary: It can be beautiful to work with until it snaps you right out of it. At this point I'm not thrilled to work in it, but I'm not mad either and will probably see the day I write more of it.
Racket
The Good:
- For me, the most readable. I'm not sure if this is a reflection of learning it first, but there's something so clean about it. While you can always drop into the more complex, the core basics read very well.
- Incredibly powerful macros. They are much more syntactically complex and resources for learning the ins and outs are hard to find, but once you get them, the world is your oyster.
The Bad:
For whatever reason, all of the practical packages you want simply don't exist yet, or if they do, break the cleanness bare Racket gives you in ways you probably shiver looking at. It's my only real negative, but the implication basically is that for any serious project, unless I want to write the tooling I want myself from the ground up, it's basically not an option.
What really sucks about this the most to me is that of all the languages, writing good libraries for Racket is the easiest given the powerful macro tooling. All the bare bones are there, they just need to be composed together ands abstracted the right amount while keeping the simplicity that makes Racket beautiful. When said aloud, it's a big ask. But IMO Python has done so well accomplishing this with things like requests, flask, etc - what's stopping Racket?
Oh, and the IDE (DrRacket), while deceptively powerful, still needs a competitor badly. I'd love to see a Jetbrains IDE for Racket, though of course I know that's not happening anytime soon.
Summary:
I wish I could write everything in it, but the tooling and IDE just aren't there. Currently my focus is actually now on that tooling itself [2].
Functional languages I've looked at but have not written anything serious in:
Haskell: I get the appeal from a theory perspective, but the readability for me is absolutely terrible. Things are more verbose where they shouldn't be and too terse in the reverse. I haven't spent enough time to comment any further, but that alone was enough to steer me to explore other functional language options.
Clojure: The community seems great and I am excited it's gaining traction (and building out that tooling that Racket is sorely missing), but I wonder why these two have to be separated from each other. At the core of the language, it seems to me on cursory glance that clojure's syntax is just ever so slightly worse than Rackets, though less annoying than the quirks of OCaml. I can't help but wonder what would be if the clojure community instead wrote tooling for Racket (and some do!). For everyone involved closely with Clojure, I'd love to hear more about why it needed its own language. I know being on the JVM was big, but beyond that I'm pretty fuzzy.
Functional Programming in general:
While there are cases where imperative and object oriented programming make sense, I truly wish a day comes where the word mutation is taboo, imperative style is used sparingly when needed, and code readability and library design is valued above all else in industry. And there are good developments with things like Clojure, Redux, and the big languages embracing functional style more. Still, the difference of how I program in each style is truly incredible, and while my code is objectively better in the functional style, I currently rarely get to use it for projects, professional or personal.
To anyone who hasn't tried functional programming, I couldn't recommend it enough. HtDP or this book both seem like good places to start.
[1] https://www.htdp.org/2019-02-24/index.html
[2] In an attempt to improve the tooling to Racket itself, I am working on two libraries: A web framework and a basic ORM for structures. Both are in highly unstable condition at the moment so it is not recommended to try and contribute to them at this point, but I am targeting May to have stable 1.0 releases. hopefully expect to see a Show HN :) They are loosely inspired by Flask / SQLAlchemy, meant to address similar things in Racket. Links at [3] and [4]
[3] https://github.com/adjkant/web-sourcery
[4] https://github.com/adjkant/sql-sourcery
[5] Syntax Example for [3] and [4] together: https://github.com/adjkant/web-sourcery/blob/master/examples...
This is not a quirk but a feature, just like rec keyword. It could be easily removed, but it's there for a reason, it makes code easy to reason about from the first glance.
How are the platforms, libraries and future direction?
I am quite far from using it professionally (or in anger, as they say), but my next toy-project/game will probably use ReasonML or ocaml.
ReasonML project tries to make most things work out of the box for particular purpose. It targets mostly frontend-devs and maybe node-js devs. I.e. I played around with buckle-script, and having it set-up on my machine was literaly just typing `npm i -g bs-platform` and adding reason-vscode extension to my visual studio code.
What I really like is the recent-is addition of esy.sh [3] that helps with compiling ReasonML trough ocaml to binary executables (previously mostly the buckle-script compilation to js worked out of the box)
Syntax is nicely familiar, type-error messages are approaching Elm-level of awesome a everything works, as long as you don't stray too much from the beaten path.
If you do stray, it can get awkward. Sometimes I didn't know if I should read documentation for reason, bucklescript or some other part of the underlying ocaml toolchain. Two examples:
First I played around with redoc [4] beautiful tool for generating documentation that contains embedded, compiled-to-js, executable code samples. But I could not compile my document, even though it looked like the example in the repo, I was getting an error about function not being present in a module.
After several hours I realize, that the example documentation was compiled with previous version of the tool, and that with lates instead just calling Module.f() I need to be calling PackageName.Module.f()
Second, I wanted to use esy.sh to work on those 99 problems. And I wanted to write tests for my solutions. I google around and find an ocaml unit-testing library [5] and it took me about an hour to figure out how to configure it. In the end it wasn't hard, it just took me quite some time to notice esy.sh is using dune [6] build system underneath and that I need to change the dune configuration files. I probably wouldn't need to do this for other libraries, but test-framework was using the ocaml extension-points (something like macro-system) and you needed to enable it in the build-config.
So far I still like it and it is not more complicated than dealing with other js-ecosystem frameworks :-) But going from node.js ecosystem to golang at work, I would preffer the go-style "just run this binary and you are fine" to js-ecosystem style "here is a rube-goldberg machine of build-systems".
[1] https://ocaml.org/learn/tutorials/99problems.html [2] https://reasonml.github.io/docs/en/overview [3] https://esy.sh/ [4] https://github.com/jaredly/redoc [5] https://github.com/janestreet/ppx_inline_test [6] https://dune.build/
We use BuckleScript (without Reason, plain OCaml) to deliver a few web apps meant to run on mobile. The experience is decent and the lack of multicore support for OCaml doesn't really matter when you execute in JavaScript.
OCaml is mostly a fine language, but anyone familiar with a more ergonomic language like PureScript will miss a ton of features (higher-kinded types, type classes, etc.. People usually like to say you can make up for it with functors, but it's not really the same at all; much more cumbersome and less ergonomic. We do use functors a lot, but to say they're doing the same thing is being a bit too liberal, IMO). There's been a plan to implement ad-hoc polymorphism for many years now and honestly I don't think it'll see the light of day. Most things in OCaml move at glacial speed and I think it's better to look elsewhere in languages that already have these features if you're not already invested.
BuckleScript obviously adds JS interop on top and with time I've come to resent how it's done. It's pretty verbose, doesn't fit in with the rest of the language and is generally speaking pretty fiddly. PureScript has much better interop that looks exactly like the rest of the language, no ugly decorators needed.
Over time I've come to realize that even though I may want to use PureScript for projects, it's probably not going to happen with the small community and higher degree of investment needed for people. So while PureScript is superior to pretty much all "write something that runs in the browser" solutions right now, we're not very likely to use it (yet). BuckleScript is mostly the same and on top of that it's based on a worse language moving slower, with an interop layer that's worse.
With that in mind, the sanest way to do it is just to use TypeScript. It rates better in almost every metric that has to do with "Is this usable for us?". It lacks the niceties like HKTs, etc., but can pretty easily make up for most other features. I realized this after working on a talk about designing programs via the type system and being able to do most things with TypeScript that I'd expect to have to use PureScript/OCaml for. There are still parts missing, but those parts are missing in OCaml too and PureScript is the only one that gets abstraction completely right on a language level, barring dependent types.
There's a way to simulate HKTs via type defunctionalization which is applicable to both OCaml and TypeScript, so you can approximate it, and there's a library for it out in the wild called `fp-ts`.
With TypeScript, the interop story is obviously as good as you're going to get. With strict mode turned on (it is by default) it's not going to allow nonsense and in the past few years it's gotten features that allow you to do tons of type system modelling. I've already made strides in our code base to support things that we're doing in OCaml and to a large extent it's mostly the same kind of feeling of "It compiles, so it's very likely actually correct".
So all in all, since I discovered how to express more interesting type concepts in TypeScript (I was previously convinced, mostly by FUD, that this wasn't (yet) possible) I'd rather we don't add any more OCaml projects as I think the tradeoffs just aren't the right ones anymore: OCaml's (possible) advantages in the type system are few and they don't at all make up for the barrier to learning (even though OCaml is a very simple language), worse tooling or worse interop.
Edit: I should also add we've had a ton of odd issues with building our OCaml code via BuckleScript. Rollup, for example, plain didn't work at all even though there's no logical reason it should be failing. One of the base/prim files was failing to build and no one on either side could figure out why. We currently run webpack without a custom bs loader and precompile the .ml files instead. Installing the `bs-platform` package in our docker containers with `yarn` also failed for unknown reasons, so for docker we also precompile the entire `bs-platform` from source.
From reading, the Reason team have had great success using react from Reason (so OCaml) even without manual optimizations, because of immutable data (even compared to immutable.js). Take that nugget with a pinch of salt, though, because I didn't hear it recently and I'm not sure I ever saw real numbers behind it.
I would just like to add, yes at the immediate level you can trivially use JavaScript code in TypeScript, but when you are within the TypeScript world there are a few issues:
- You need typings for the JavaScript libraries you're using, which are fortunately abundant but you have to keep an eye on their quality, as lots of things can be typed as `any` or `function` which is effectively saying 'whatever'.
- The process of getting typings is not as uniform as you'd expect. For example, you want to use the Web and related APIs? They are behind a compiler flag, not an import: https://www.typescriptlang.org/docs/handbook/compiler-option... . In BuckleScript all available third-party APIs are done uniformly with imports or hand-written typings.
- You want to write typings for a module? You'll need a `.d.ts` file to contain them. In BuckleScript, JavaScript typings and your OCaml/Reason code are uniformly contained in implementation/interface files. There's no distinction.
Regarding the bundling, it's hard to tell exactly what that was, but generally your strategy of explicitly splitting up the compilation steps is a good one.
Regarding the Docker issues, that is unfortunate, I wonder if you have tried one of the pre-built BuckleScript images? E.g. https://hub.docker.com/r/andreysenov/bs-platform