Bucklescript

Wow there’s a version 3? That’s gotta be new. ^.^

Mine is only:

$ ocamlmerlin -version
The Merlin toolkit version 2.5.0, for Ocaml 4.03.0

I’ve just spend the last week familiarizing myself with bucklescript. So far I’ve encountered several pain points:

  1. The Reason syntax seems to be preferred by the community. I tried bucklescript to get better at OCaml, but most libraries (e.g. bindings, such as one listed in reasonml-community org) are written in Reason. It’s a pain to context-switch between the two, as Reason drifts further and further away from OCaml syntax. Reason-tools help in the browser, but that’s extra effort needed for each file in the codebase. I started to feel kind of like a douche that doesn’t want to “fit into” the community by consciously using different syntax.

  2. The docs are rather on the incomplete side and community examples are still rare. I’ve had a hard time trying to write bindings for Chrome Extension API, mainly determining what types should I use for the external declarations and how to deal with optional args. I also have no idea whatsoever on how to utilize, for example, Js.Promise, and digging the BS source was confusing, I can’t seem to find the definitions of the included modules.

  3. Is it possible for Bucklescript to output single file from multiple sources, just like how elm-make does it? Or do we need to introduce e.g. Rollup for that? So far the compilation outputs many files with requires. I’m not keen on setting up another bundler, having touched them in my JS endeavors can’t say I liked them.

  4. One time I forgot to switch to 4.02.3+buckle-master and I had no idea why my merlin integrations did not work. I ended up coding in darkness, and having to inspect the compiler and JS output closely.

  5. The compiler messages are not too helpful. I’ve got tons of “Syntax Error on file x line y”-ish messages, mainly when I was dealing with point 4. This might be an OCaml problem, though. But I’ve written OCaml before and the errors didn’t seem to be this confusing. BetterErrors installation page recommend installing reason-cli which I don’t see the reason to if I don’t want to use Reason, which brings me back to point 1.

I realize that many of these things could be solved by asking in the Discord (that’s their preferred channel), but I’m the type of person who don’t really enjoy real-time communications such as chat platform or IRC. I’d prefer a more asynchronous method of communication…

As for the docs and examples problems, I completely understand that it most likely are a problem of manpower. But still it kinda hurts the initial jump. I’d like to help, but I feel like the resources to get the beginner me into the context where I could do anything meaningful are still not there yet.

4 Likes

Overall it took more than i guess 24 hours of recompilation, debugging and tears to finally make understand that i need that particular ocaml version to make it work :joy:

Actually all you really need is
opam install BetterErrors
And then instead od bsb -make-world for building do that bsb -make-world 2>&1 | berror

And an example output:

[1/1] Building src/overbots.cmj /Users/bartosz/Documents/OCaml/overbots/lib/es6/src/overbots.js src/overbots.cmi
FAILED: src/overbots.cmj /Users/bartosz/Documents/OCaml/overbots/lib/es6/src/overbots.js src/overbots.cmi
/Users/bartosz/Documents/OCaml/overbots/node_modules/bs-platform/bin/bsc.exe -bs-package-name overbots  -bs-package-output es6:lib/es6/src -bs-assume-no-mli -bs-no-builtin-ppx-ml -bs-no-implicit-include -I /Users/bartosz/Documents/OCaml/overbots/node_modules/bucklescript-tea/lib/ocaml -I src  -nostdlib -I '/Users/bartosz/Documents/OCaml/overbots/node_modules/bs-platform/lib/ocaml' -no-alias-deps -color always -w -40+6+7+27+32..39+44+45 -o src/overbots.mlast -c  src/overbots.mlast
/Users/bartosz/Documents/OCaml/overbots/src/overbots.ml:8 4-8
 5 ┆ let model = {
 6 ┆   notUsedYet = 42;
 7 ┆   resource_values = 1.0;
 8 ┆   msgs;
 9 ┆   bool_flags;
10 ┆   int_flags;
11 ┆ } in

Error: `msgs` can't be found. Could it be a typo?

Don’t mind the code :blush: it’s just the example of an erorr.


I wouldn’t mind trying to write reason instead of ocaml, but I really don’t like some of reason’s syntax like == instead of = or let list = [hd, ...tl]; instead of let list = hd :: tl ← this ... particulary bugs me. And the most dreadful one, instead of this let x a b = e you get let x a b => e;

On the other hand reason’s type pairList = list (int, int); is a bit more readable at first glance for me than type pairList = (int * int) list

Maybe it all boils down to familiarity with one syntax over the other, because I always hated JS and tended to run from it whenever I could. Both Reason and Bucklescript are still young, so I guess I’ll give it a try to reason for now, because I don’t think I’ll ever write in OCaml, as there are really some pain points with it, and I fell in love with Rust lately.

Update: I found another annoyance in Reason
Ocaml:

let init_int_flags = 
  let open IntFlagMap in
  empty
  |> add NoIntFlagsYet 0

Reason

let init_int_flags = 
  IntFlagMap.(empty |> add NoIntFlagsYet 0);

There is no let in syntax in Reason :frowning:

Another Update:
refmt is tool for formatting reason code. But so far it has same stupid disadvantage as gofmt has. It is not configurable, it takes input and spits output formatted with whatever insane rules someone at facebook devised.
For example

let all_resources = 
  let open ResourceMap in
  empty
  |> add Energy (module Energy : Resource)
  |> add IronOxide (module IronOxide : Resource)
  |> add RawSilicon (module RawSilicon : Resource)

get’s formatted into incomprehensible at first sight blob of code

let all_resources =
  ResourceMap.(
    empty |> add Energy ((module Energy): (module Resource)) |>
    add IronOxide ((module IronOxide): (module Resource)) |>
    add RawSilicon ((module RawSilicon): (module Resource))
  );

I’m talking here about pipe operators. But there’s open issue int the github reasons repo so it may be, hopefully, changed.

1 Like

@bobbypriambodo I had very messy system now after reinstalling all those ocaml’s and nodejs’es so in short

  1. opam install BetterErrors install rather old version
  2. to install fresher version you need to do opam pin add BetterErrors https://github.com/chenglou/BetterErrors.git#npm-1.2.0
  3. There is even newer version of code, written in reason, not vanilla ocaml in master branch, but it won’t compile due to missing files OpamSystem.File_not_found("/Users/bartosz/.opam/4.02.3/build/BetterErrors.0.0.1/pkg/META.in") but i forked this repo, and try to come with some sane solution tomorrow… maybe
  4. The actuall command is not bsb -make-world 2>&1 | berror but rather bsb -make-world 2>&1 | huh because when you install it through opam it’s binary name is huh and when you install it with reason-cli it’s berror :joy:

Sorry for my earlier wrong command, it was due to the mess in my system after reinstalling so many times ocaml and reason through different methods.

:scream:

Yes, that I was what I thought too. It’s one of Reason’s nicer points.

Why…

Thanks for diving into the fray, @sztosz!

Another that annoys me regarding point 1 on my list above is how Reason and OCaml has different convention for function names (camelCase vs. snake_case). You’d end up with a codebase mixing the two if you’re using OCaml’s builtin functions, or writing in OCaml and use Reason libraries.

I guess I’m going to stick with Elm for now…

I think the guy behind it was just really experimenting with opam :wink: And really is not into the whole ecosystem much, and much rather focus on reason installed through reason-cli. But that’s not something anyone should hold against him :slight_smile:

As much as I like Elm Architecture, the pureness, no side effects philosophy behind it don’t really click with me much. I need a language that is middle ground between “get things done” and “write architecturally superb and pure code” :wink:

2 Likes

Ok, i tried to translate it back from master which is in reaon to ocaml here: https://github.com/sztosz/BetterErrors

But I got stuck, when i do opam pin add -v BetterErrors https://github.com/sztosz/BetterErrors.git i get error:

- File "src/BetterErrors.ml", line 24, characters 30-43:
- Error: The function applied to this argument has type
-          refmttypePath:string option ->
-          customErrorParsers:(string ->
-                              string list ->
-                              (int * int) * (int * int) ->
-                              BetterErrorsTypes.error)
-                             list ->
-          unit
- This argument cannot be applied without label
- Command exited with code 2.

And I don’t know Ocaml enough to fix it. Anyway code is a mess, it was machine translated from reason to ocaml, with some hand modifications, for when translator could not understand reason.

@OvermindDL1 maybe you could be so kind and help me just to make it compile? I’d clean up the code later and try to make in sync with Chenglou’s ReasonML repo

You can mix-n-match all you want. ^.^

I prefer OCaml, it is more clean and precise. ReasonML is very… bracey…

Js.Promise is already bound by Bucklescript for you actually, in the externals libraries (forgot the name off-hand), as is a lot of the DOM and more as well. It is planned to be included into Bucklescript itself once finalized as well.

As for finding sources, it is all there. What file do you want the source for? They will pretty much all be somewhere in bs-platform/lib/ocaml.

Well depending on the output option you chose you can cat them all together. ^.^

But Bucklescript is designed for modular compiling, since that is how the OCaml compiler works, it does not actually have the entire source view in the system at any given time at all, this is one reason why its builds are sooo fast. If you output, say, es6 modules, then you can just use them in any es6-modules-supporting browser (like chrome) straight. If you want it to work on ES5 browsers then use rollup or babel or so, or if you want it to work on things like IE11 then use babel to transpile it down to ancient javascript, this is all normal javascript stuff, it is not designed to be used stand-alone but rather inside whatever build system you already use (for other javascript if any, or image processing, or css, or whatever).

Lol, your IDE did not give you any warnings? I know Atom pukes errors at me if I’m in the wrong switch. ^.^;

Are you writing OCaml or ReasonML? I don’t think I’ve ever seen Syntax Error on file x line y but I write in OCaml for bucklescript, not ReasonML.

I relate. ^.^

You should not. It should work with any merlin for OCaml 4.02 or higher (I’m using 4.04 now), that is what the PPX line in the merlin thing is for, to get it working on any version. If it is not then it sounds like your IDE is not using the .merlin files.

Eyup, entirely agree there…

Bucklescript is not really young, remember it is just OCaml with a to->JS backend. Anything for OCaml works fine with Bucklescript as Bucklescript quite literally just is the OCaml compiler with some PPX’s and plugins pre-built-in.

Oh but the OCaml syntax is quite awesome, perfectly unambiguous, you know what any structure is. ^.^

And yep, Rust is awesome, but I would not say it’s syntax is awesome. They’ve been making some really really stupid, haskell’y unreadable choices in recent RFC’s…

Yep they are special casing |> in a soon-coming version. Which of course may not work well with user-defined |>'s, but ReasonML is not type-aware, rather it is just a text munger, so it cannot do much better, kind of like the same problems as Elixir’s formatter.

But that format is ambiguous. Is it a tuple of int * int, or is it an expression that returns a single int. In the base language it does not matter, but with PPX’s and plugins it does matter. OCaml is entirely unambiguous in comparison. * defines a prod/tuple type, always in types

Bucklerscript-TEA. ^.^

.[quote=“sztosz, post:112, topic:1579”]
And I don’t know Ocaml enough to fix it. Anyway code is a mess, it was machine translated from reason to ocaml, with some hand modifications, for when translator could not understand reason.
[/quote]

For note, the refmt program converts between OCaml<->ReasonML, it can convert between both losslessly. The ReasonML compiler thing literally has refmt embedded in it and just uses it to translate ReasonML to OCaml to feed to the OCaml compiler, that is all it does (ReasonML is dreadfully simple in implementation, just a string munger is all). So if it does not compile then it would be because of any further edits that were made outside of refmt.

Cannot just use his Repo? Or why not just the native BetterErrors library?

But that is the problem! :smile: I’m all for interop, but if some of my snake_case functions will call some of the camelCase functions from my library, it will be all but consistent. I’d prefer consistencies on the codebase. Elixir and Erlang, AFAIK, still have the same conventions regarding identifiers, for example (or at least where they are different, such as variable names, one convention can’t be used at the other). That makes calling Erlang functions from Elixir a breeze for the eyes.

If I’m writing a lib using OCaml syntax, do I need to expose the API as snake_case or camelCase? If I do use snake_case, would that make my lib seem exclusive for Reason devs? Thoughts like that hinders the actual development and I would prefer not to think about it if I could…

Now that you mentioned it, I tried to look inside node_modules/bs-platform/lib/ocaml and it’s all there. Funny thing is, the github file tree doesn’t seem to include those files. That was the first place that I looked.

What if I don’t use JS :smile: Yeah, I end up using rollup since it needs the least configuration and I wanted only a bundler anyway.

Sadly vs-code is insanely quiet on errors, that’s why figuring this out why merlin don’t want to cooperate took me so long. And after all, i din’t figure it out all yet, but it came down to bucklescript not wanting to compile with 4.05.0, falling back to internal copy of 4.02.3+buckle-master, having merlin 3.x installed, and some cryptic error i found in Atom after a while only about first Atom not working with merlin 3.x and then after fixing that errors about some version mismatch of ocaml here and there (i don’t know what exactly was not right :joy:) Anyway I learned about a given language and ecosystem in a short time more than ever before :wink:

Oh, I din’t mean the language and compiler per se, rather the community around the idea of bucklescript specific, excluding ocaml, and adoption to be specific. It’s like with Python 3, it was just python with some good changes, but when it came out the adoption rate was very small, it took few years to python 3.3 be released when the adoption really started to grow.

Damn, I didn’t think of that, and when you put it that way i really makes sense. I wish i was smart enough to see those things by myself, really :confused:

Can you elaborate more, I don’t know Haskel, and Ocaml i know just a tiny bit. And to be honest, I really like to read what you think about those things, because you’re smart guy with ton of experience, and not many out there write about those things.

I don’t know why refmt couldn’t translate reason in two places, but it’s maybe i used it as in browser extension, I try more later this week, maybe on weekend.

Anyway, ReasonMl → Ocaml translation produces so much brackets it’s hopeless. :stuck_out_tongue:

The repo is written with reasonml, and it’s written so it can be integrated into reason-cli. There are no files left so it can be installed through opam. And the native “old” BetterErrors library does not have some of the more recent fixes, and uses a huh binary name :slight_smile: As an exercise I wanted to convert it back to opam installable more recent. ocaml version, mirroring changes made in original library, but got stuck. Anyway, i’ll sit on it more on weekend, I might have been to tired when I gave up on sunday :slight_smile:

Ah but unlike python you can still use the underlying OCaml. ^.^

When using bucklescript, if you write your sources well (proper external writing and so forth) then you can compile to javascript or you can compile to native code, how’s that for speedy server-generated templates. :wink:

They are in the RFC repo for rust, but there are a lot of growing RFC’s to try to work around some of the language warts by introducing new concepts, which is entirely the Haskell style. Interestingly they even have a lot of the same kind of type class issues that Haskell has and so they have to introduce an every-growing set of syntax enhancements to work around it when something like OCaml’s module system solves all of those issues if they would just follow that style (with the bonus that the Rust compiler would have been a lot faster than it is now, all these haskell’isms sneaking in is making it sooooo sloooow to compile in code that uses them). I can rant about specific things some other time, maybe in a Rust thread, but I’m too busy right at the moment because at-work. ^.^;

Heh, brackets?

Ah, yeah you could use refmt to convert it to ocaml then compile it straight, as long as it does not make any calls into the reason library (which it probably does, hmm).

Reason to OCaml gave me this | ((Some ((a,b)))[@explicit_arity ]) -> ((String.trim a), (Some (String.trim b))) in one of matching branch, and it would not compile until I changed this into this | Some (a, b) -> ((String.trim a), (Some (String.trim b))) and here’s orginal Reason code | Some (a, b) => (String.trim a, Some (String.trim b))
The quantity of brackets in translated code scares me :confused:

If this would not compile then you are missing a ppx, probably Bucklescript’s PPX (which works fine in normal OCaml, link it in).

Lol, eyup, ReasonML has different precedence than OCaml in many areas so it does a lot of wrapping for ‘safety’ since it does not know types (only text).

EDIT: Yep confirmed, [@explicit_arity] is supplied by the bucklescript ppx. It only makes sure to uncurry any calls through this, which is not necessary on the normal OCaml compiler anyway (bucklescript does a no-op when compiling to native with it, so no change). You can remove it, or just link in the bucklescript PPX (bsppx.exe I think it is called, feed it as a PPX to the ocaml compiler like any other PPX) and it will all work regardless without changes. :slight_smile:

Hi @OvermindDL1 @sztosz, quick question: is there a convenient way to convert a < .. > Js.t into an OCaml record? Or rather, is there a function that works with < .. > Js.t the same way that { foo with bar = "something" } works with records?

I’m using bucklescript-tea and currently have a < .. > Js.t as my model. Wondering what would be the best way to implement the update function…

I found Js.Obj.assign but it seems that it mutates the target object, I wonder if it’s a good idea to use it.

Great posts!

Now I’m deeply interested in learning bucklescript and, hence, OCaml.

@OvermindDL1, would you please suggest me some resources (books, tutorials, anything)
I could use in order to learn OCaml from scratch?

I payed a visit to OCaml’s site and saw a bunch of books. Cannot decide on which one
to buy.

Any help is greatly appreciated. Thanks to you all!

2 Likes

Real World OCaml is generally recommended as the only really good OCaml-specific resource afaics, and that’s free online.

Personally, I just did Coursera’s Programming Languages Part A to get better acquainted with the Hindley-Milner type system (it’s in ML); some of it was a little basic at first (mid-level college course, so aimed at those who’ve programmed using OO/Procedural), but overall I’d highly, highly recommend it if you want to understand how everything works, most of what it teaches is immediately transferrable to OCaml.

1 Like

You really should just use an ocaml record. Using a javascript object does not gain you anything in speed and just makes using it a lot more painful. ^.^;

I would definitely not use a Js.t as my model in any case. In general you should not be using Js.t anywhere, especially if you want to compile it to native code since the javascript types will not exist then.

But if you really want to use it as your model, I’d suggest lots and lots of helpers and copying it around, it will be painful (really, just use a normal ocaml record ^.^).

Absolutely not, this will break snapshot support (not exposed yet).

Actually one of the best original books I’d say is Real World OCaml, which is free online, it is old though (which is also why it is free now) and has some old bits in it like assuming caml4p is available and such (that was OCaml’s old macro-like system before PPX’s existed). But even the tutorial on the OCaml website I find pretty top-notch. :slight_smile:

Overall, I mostly learned from the official Spec (I try to do this with every language).

As for actual physical books, not a clue honestly, I learn primarily by reading code and language specs, not really books (they do not really ‘stick’ in my mind unless I’m doing it as well).

Oh if you don’t already know Hindley-Milner typing (it is so easy) you should. Heck, I tend to make a hindley-milner typed mini language in new languages I learn as it is a great task to see how a language holds up to ‘real code’, real as in parsing, catching errors, modularity, etc… etc… (Elixir is… not a great language for that sad to say…, OCaml is fantastic though, OCaml is such a fantastic language that other than C itself, OCaml is used to make more compilers for languages than any other language out, other than C of course ^.^).

1 Like

Yep, that’s why my first question was how to convert a js object to record :smile: The data from the API is a JSON, and preferably I can convert it somehow into an OCaml record… But I’m lost as to how to do it best.

Bucklescript exposes an API that accesses the DOM’s json parsing functions to let you parse it into an OCaml format. Bucklescript-tea gives you a set of encoders/decoders for doing the same (and that I can port to native code with relative ease when I get around to doing it), and if using bucklescript-tea you really should learn those Decoders. They are nicely composable, safe, and output into whatever format you want. :slight_smile:

The Elm docs for their JSON decoders work for mine as well since I emulated their API almost precisely. :slight_smile:

All right, I’ll give Decoders a go :slight_smile:

Bucklescript-tea so far is fun to use. I’ve only done elementary Elm stuff and the knowledge translates nicely. I was bummed that I couldn’t use ports and subscribing to the app from outside world (pushMsg only works for sending into the app, right?), but it lets me learn about Cmds. What’s neat is that I’m calling the main function from bucklescript and it gives me the flexibility of sharing code, something I don’t think you can do with Elm + JS :smile:

Thanks for it, @OvermindDL1!

1 Like