
Programming a space invader in OCaml and OpenGL: lessons learned - juliendemangeon
https://marmelab.com/blog/2020/02/21/ocaml-and-opengl-in-practice.html
======
sweeneyrod
ReasonML is just an alternative syntax for OCaml. Often people use "ReasonML"
shorthand for "ReasonML, converted to OCaml, compiled to Javascript with
BuckleScript" and "OCaml" for "OCaml compiled natively" but it's perfectly
possible (and actually really easy) to use OCaml with BuckleScript and Reason
natively.

Also note that despite the "O" in the name, OOP in OCaml is rare (for
instance, there isn't any in the code in the article).

~~~
elcapitan
What's the state of using Reason natively? Last time I checked all the
documentation and examples where towards the reason/js/react ecosystem. Are
there any good native code projects written in Reason to look at for
reference?

~~~
sweeneyrod
People don't do it much, but if you're using the most popular build system
(dune) you can replace your .ml files with .re ones and it will Just Work. So
you can use OCaml projects for reference (I think anyone who's doing Reason
should have a vague grasp of OCaml syntax, it's not really that different).

~~~
firethief
> I think anyone who's doing Reason should have a vague grasp of OCaml syntax,
> it's not really that different

Especially if you're targeting native, so all your dependencies are written
and documented in OCaml syntax.

------
kingkonz1
> it is very complicated to install the base stack to develop in OCaml

I'm not sure why the author decided to use such a complicated setup using Make
and Docker.

They could have just ran `opam switch create 4.09.0`, which would have created
the compiler, then `opam install ...` for the dependencies.

Still, an interesting article. I wouldn't recommending using OCaml/ReasonML
for gamedev since getting it to work with Windows is pretty much impossible.

~~~
pnathan
It is complicated to figure out a straightforward way to do builds in OCaml.
If you're not tapped into the Current Way, you kind of have to read this blog,
read that fragment, piece it together. I worked it through for a project of
mine, and it was unusually unpleasant vs, say, Haskell.

Docker because, presumably, he didn't want to dink with installing it locally.

~~~
ernst_klim
> It is complicated to figure out a straightforward way to do builds in OCaml

Sorry, what is not straightforward in building with `dune`?

~~~
srpablo
Some things I ran into:

\- How do I run tests? Oh, you can't really dune runtest on executables, only
libraries. So how do I run tests on executables? Make a library with the core
of the functionality and wrap it, I guess. Wasn't obvious.

\- Tree-like heirarchy of dune files confused me since it wasn't really
explained anywhere. I wanted my source files in a `src` directory; most of the
examples assume your `dune` file is just hanging out with the source in the
toplevel.

\- Does `dune` handle dependencies (like I would declare them in a
rebar.config, mix.exs, or a package.json)? Not really, it plays with opam,
requiring one to read a bunch of docs for opam, and its files, and where they
should go.

\- The syntax of the dune files changed a bit from when I started the project
(it was just moving from jbuilder -> dune). Less of an issue now, though I
still question the use of s-expressions.

\- Can I load an interactive shell with the definitions? There's a command in
the documentation for it, but it didn't work quite right when I tried.

\- If you look at other projects, you'll see they use opam files differently
from one another.

\- How do I add a dependency in my current switch (something like npm --save
install <library>?) I tried `opam install --deps-only`, but it complained
sometimes about the state of uncommitted files.

I understand why it's like this (trying to tie together a ton of use cases)
and love developing in it after it's all said and done, but there are a lot of
rakes to step on.

~~~
ernst_klim
> Tree-like heirarchy of dune files confused me since it wasn't really
> explained anywhere.

It's explained in project layout [1]. And it's not a tree like, it's basically
flat, since it doesn't matter how deep in the file system your dune file is
buried.

It's like `meson` in its concept.

> Does `dune` handle dependencies

It doesn't, and so neither do `meson`, `cmake` and most other build systems.
As you may want to use various ways of installing dependencies: native
packages, manual compilation, opam, esy.

> Can I load an interactive shell with the definitions?

Yes, you can run `dune utop ./path`

> If you look at other projects, you'll see they use opam files differently
> from one another.

Dune is independent of opam, opam file is needed to publish your package to
opam. You can specify packages with dune without creating an opam file, see
[2]

> I tried `opam install --deps-only`, but it complained sometimes about the
> state of uncommitted files.

If you use opam, you can do `opam install --deps-only`, or `dune build
@install`, which would suggest a command to install missing packages.

[1]
[https://dune.readthedocs.io/en/stable/overview.html#project-...](https://dune.readthedocs.io/en/stable/overview.html#project-
layout)

[2]
[https://dune.readthedocs.io/en/stable/concepts.html#package-...](https://dune.readthedocs.io/en/stable/concepts.html#package-
specification)

------
pjmlp
The underlying OCaml naturally.

Having learned ML via Objective Caml's predecessor, Caml Light, ReasonML seems
to be just sugar coating for those with aversion to ML classical syntax.

~~~
hellofunk
> ReasonML seems to be just sugar coating

That’s exactly what it is, no secret about that, that’s the goal of that
project.

~~~
pjmlp
Which begs the question why not learn the real deal, instead of adding another
layer to debug.

Programing isn't poetry.

~~~
toolz
I disagree, code is for and by humans. There are likely very real runtime
consequences to having code that is "ugly", because the next human to touch it
will value it less and do a suboptimal job.

Of course all of this is highly subjective, but my understanding is that it's
important to consider if you want highly successful projects.

~~~
Gene_Parmesan
> code is for and by humans

I think this is very much domain dependent. Generally the more important
efficiency is, the less I find this to be true. And really, code is always for
the machine at the end of the day.

Additionally, if an added code layer makes the code more difficult to debug,
that's also making the code worse for humans. Note I'm not arguing for or
against whether that's happening in the case of OCaml/ReasonML, just making
the point.

~~~
sshine
> code is always for the machine at the end of the day

On the other hand, code is read much more than it is written.

You may also regard efficiency as a necessary evil if the cost is readability.

Ultimately the winning strategy in this regard is achieving "free
abstraction", making code that is both readable and efficient. Different
languages aim at this. C++ has been best at this for most of history. Haskell
and Rust are competing at this now as well.

OCaml isn't exactly efficient because of free abstraction, but because of its
extremely simple translation strategies. OCaml's "flambda" compiler extension
wasn't released as stable until 4.03 (2016), which means that before that,
very little high-level transformation was happening.

My experience is that OCaml programmers care about low-level optimization, for
good and for bad.

For example, in this StackOverflow response there are two examples of a
function 'partialsums':

[https://stackoverflow.com/questions/37694313/make-ocaml-
func...](https://stackoverflow.com/questions/37694313/make-ocaml-function-
polymorphic-for-int-lists-and-float-lists/37710287#37710287)

The one that Jeffrey Scofield provides is essentially more efficient because
the accumulated value is a list, just like the end result, whereas my solution
accumulates a tuple, which means every iteration of the fold involves boxing
and unboxing that tuple. An optimizing compiler working at a higher level of
abstraction might figure that out and re-use the memory of the tuple, but no
sir.

------
hajile
ReasonML is adversely affected by it's Ocaml base if anything. StandardML
would have been a much better base (they went with Ocaml because it was a
lower-effort to implement and Facebook has a lot of Ocaml devs).

The first bad part is that Ocaml _does not_ have a language spec. The
implementation is the spec. This puts them at the whim of the Ocaml devs with
no other options. SML is a standard with several good implementations.

Ocaml requires different operators for float and integer arithmetic. SML
assumes an integer type unless you include a decimal. Both need to be replaced
with module typeclasses (and both are working in that direction). For SML
though, your existing code can continue to just work as it did before while
Ocaml will be left with lots of `+.` or `*.` operators everywhere.

Another mistake that bleeds through is mutable strings. This is an outgrowth
of all arrays being mutable. Immutable strings allow efficient representation
as ropes instead of normal arrays (along with other optimizations that are
good for GC'd languages). SML has immutable strings and vectors along with
arrays (which you can still use as mutable strings of characters if you need
them).

For their React-like goal though, Ocaml's lack of anonymous record types is
the absolute worst. In SML, I can just type up a record and pass it in (no
type definitions required). When passing in stuff to a component, you have to
have all these named arguments. In SML, I'd just pass an anonymous record and
destructure it in the function -- Just like in Javascript, but with types
behind the scenes to keep everything straight.

Writing a custom parser for Babel like typescript or coffeescript have done
would have been better for the language design and independence.

~~~
vphantom
FYI strings are now immutable in OCaml. It was a compiler option starting with
4.02 and it has become enabled by default since 4.06.

~~~
hajile
That's nice to know. On the flip side, that's not nice for interaction between
older code that wants to mutate and newer code that wants to "copy". If the
"copy" code is actually copying all the time behind the scenes (something it
has to do if you are compiling with mutable strings for backward compatibility
with the rest of your codebase), that's a huge performance hit.

------
alharith
I am confused by this article. It went an entirely different direction based
on the question proposed. It was all entertaining, nonetheless, but besides
some passing comments to Reason’s documentation being better, the question
does not seem to be explored much by this article. It’s a good article and
should be renamed to maybe “Getting started with OCaml and OpenGL”

~~~
juliendemangeon
You're absolutely right. I wanted to justify my exploration by way of
comparison between ReasonML and Ocaml.

The title ("Programming A Space Invader In OCaml and OpenGL: Lessons Learned")
still fits with the content, don't you think?

~~~
alharith
I see that now, yes I do. I think maybe the headings and subheadings are also
throwing me off as well. I wasn't sure if I was reading one of those types of
blogs that have a continuous feed of articles on one page.

------
cpursley
Marmelab makes the pretty awesome react-admin library for autogenerating admin
interfaces:

[https://github.com/marmelab/react-admin](https://github.com/marmelab/react-
admin)

I'd love to see an official Marmelab adapter so I can build my admin
interfaces with ReasonML.

~~~
Kmaschta
We already have hard time migrating it to TypeScript :p How such an adapter
might work?

------
ernst_klim
> I choose a more undisclosed tool (obuild) because of its simplicity and its
> _brief documentation_

> And although some tools exist to make our life easier, they are _poorly
> documented_

Seems like author choose something poorly documented. Dune build system [1],
opam [2] and esy [3] package managers are very well documented.

[1]
[https://dune.readthedocs.io/en/stable/](https://dune.readthedocs.io/en/stable/)

[2] [https://opam.ocaml.org/doc/](https://opam.ocaml.org/doc/)

[3] [https://esy.sh/docs/en/what-why.html](https://esy.sh/docs/en/what-
why.html)

------
vzaliva
This article title and conclusions are misleading. It shows how to write a
simple game in OCaml and then proceeds to conclude that ReasonML is better. I
would expect the author to have the same game implemented in ReasonML for
comparison to show us why. Otherwise, it is not very convincing.

~~~
gameswithgo
having done some OCaml and F#, where F#’s syntax is more like reason, i think
it is pretty clear that OCaml is a fascinating language but what does impose
some weird syntactic work that both reason and f# relieve you of. probably to
someone sufficiently used to ocaml it doesn’t matter but it can hurt adoption

~~~
nv-vn
I don't see how you can say F#'s syntax is more like Reason. OCaml and F# have
nearly identical syntax for the core language, with the few differences being:

1) places features diverged

2) modules

3) F#'s "light" syntax mode

None of these really seem like Reason at all. Do you have any specific places
where they're similar?

------
rwmj

        dnf install ocaml ocaml-lablgl-devel

?

Anyway here's a toy game I wrote in GL + ODE + OCaml years ago:
[https://github.com/fccm/OCamlODE/blob/master/examples/katama...](https://github.com/fccm/OCamlODE/blob/master/examples/katamari.ml)

------
fhcoso
Interesting but why talking at the end about F#, Scala and not Haskell
(general-purpose, statically typed, purely functional programming language
with type inference and lazy evaluation) wish is more old and mature than
OCaml and F#: \- Haskell: 1990 \- F#: 2005 \- Scala: 2004

------
adamnemecek
Between reasonml and elm which one should I pick?

~~~
weavie
If you've not used an ML before, start with Elm.

Once you feel you have that sussed - it shouldn't take too long - pull up
ReasonML and give it a go. Use the Bucklscript-Tea library - the architecture
will be familiar from Elm.

Then give Reason-React a go.

Once that itch has been scratched move on to PureScript for a whole new rabbit
hole to fall down.

~~~
adamnemecek
I mean I'm familiar with ml and haskell, I'm more interested in what's the
best platform for building things. The language itself is only a part of the
equation.

~~~
azangru
I believe Reason is more practical (easier FFI), but as for building things,
at this point it's probably Typescript :-)

Reason still hasn't had its async story ironed out, and has poor support for
Unicode[0]

[0] "ReasonML strings are encoded as UTF-8 and not compatible with
JavaScript’s UTF-16 strings" (from [https://2ality.com/2017/12/basic-types-
reasonml.html](https://2ality.com/2017/12/basic-types-reasonml.html))

~~~
yawaramin
> Reason still hasn't had its async story ironed out,

Reason's async story is pretty simple: use promises until the let-syntax (like
generalized do-notation) lands, then use that.

> "ReasonML strings are encoded as UTF-8 and not compatible with JavaScript’s
> UTF-16 strings"

Unfortunately this is inaccurate, and you left out a significant detail from
the article. Let's go point by point here:

\- Reason strings are not encoded as UTF-8, in fact they aren't encoded at
all.

\- But, when targeting JavaScript using the BuckleScript compiler, strings can
contain Unicode characters and use the JavaScript runtime's support for
Unicode.

\- This is not a short-term workaround, but the intended solution.

------
SkySkimmer
>Even though it is known for its robustness and its large standard library

I thought the ocaml stdlib was relatively small though?

