Advice on standard library to use


#4

Have you looked at the OCaml-containers wrapper for BuckleScript? https://github.com/cxa/bs-containers-core


#5

Hi @Rogs. This looks incredibly interesting but it is, shall we say, rather sparse on documentation. Do you have experience using it in production or even better can you provide some examples?


#6

The Containers library does look comprehensive: https://c-cube.github.io/ocaml-containers/last/containers/index.html

Curious to hear about experience from people using it in production and how it compares to Base and Belt (esp. functors vs phantom types).


#7

@pbiggar Have you taken a look at bucklescript-tea ? It’s a bucklescript library that mirrors the Elm 0.18 Api mostly, but there are a few differences. Unfortunately, there’s no documentation yet, you just have to take a look at the code. (Not too hard) It’s mostly naming things, like in the Html library, you use class’ vs class, input’ vs input, etc … Actually now that I think about it, its uses ' a lot. Also, the starting program function is named differently.

You can take a look at these

  • Bucklescript Tea Chess tutorial
  • Bucklescript-tea-game-overbots tutorial

for examples of how to use it. (They are kind of complicated for me though)

I really like the library though since I can still use the TEA (The Elm Architecture) and their API mostly.

I also just decided to retheme the standard library docs using bucklescript-tea and you can take a look at the code here if you like: https://github.com/seadynamic8/ocaml-modern-docs
or the actual website if you need a prettier looking docs: https://www.streamingspring.com/ocaml/docs/
Just keep in mind that its the latest version of OCaml (4.07) and not the one Bucklescript uses (4.02.3) and I don’t have any of the submodules listed there yet. (Work in progress)

I am also planning on writing a guide for people going from Elm to Bucklescript-tea (hopefully) :slight_smile:


#8

Yes, we’ve been using Bucklescript-tea. Seems fast and complete, though very lacking in docs. We’re going to start upstreaming some stuff to it soon.


#9

In general Belt is more efficient than Base or Core and produce less JS code so that it can be put in production in some strict environment, it is indeed less feature rich.

I am a bit confused about t-first/t-last argument since Base also adopts t-first convention

Finally, having read lots of github issues, it seems that contributing to Belt involves a lot of work: there appears to be a lot of context involved, and long and contentious discussions, so I’m not confident that we will be able to upstream the work we do to mitigate our problems.

I hope this can be improved, but would be also interested in some concrete feedback


#10

We are going to upgrade to OCaml 4.06.2 soon


#11

@bobzhang Would it be possible to use Belt in native OCaml? I’m presuming phantom types will work well in both Javascript and native runtimes.


#12

I didn’t actually know Core was t-first because it is also t-last: almost every function only has 1 positional argument, and the other arguments use labels.

Consider https://ocaml.janestreet.com/ocaml-core/latest/doc/base/Base/String/index.html. Here are some signatures chosen arbitrarily from the middle:

val lsplit2 : t ‑> on:char ‑> (t * t) option
val rsplit2 : t ‑> on:char ‑> (t * t) option
val split : t ‑> on:char ‑> t list
val split_on_chars : t ‑> on:char list ‑> t list
val split_lines : t ‑> t list
val lfindi : ?⁠pos:int ‑> t ‑> f:(int ‑> char ‑> bool) ‑> int option
val rfindi : ?⁠pos:int ‑> t ‑> f:(int ‑> char ‑> bool) ‑> int option
val lstrip : ?⁠drop:(char ‑> bool) ‑> t ‑> t
val rstrip : ?⁠drop:(char ‑> bool) ‑> t ‑> t

Every one of those can has only positional parameter, and so can be trivially curried, and piped to using both |> and |..


#13

This. I did a few problems on Excercism with Base and never once thought it to be t-first (I did all the piping |>). It seems like Base is designed to be t-ambivalent. Given that Belt uses labeled params anyway, maybe it makes sense for it to adopt that approach too.

Bear in mind that I’m a total noob in FP though.


#14

See this blog: https://blog.janestreet.com/core-principles-uniformity-of-interface/, t first was mentioned explicitly. We follow the same convention for various reasons, it actually will generate more efficient code in some cases in the future (due to that type info flow direction)

Labels are introduced when you have several arguments with the same type, otherwise it brings too much noise in the type signature.

For the universal code gen, |. is also more efficient than |>


#15

I was not saying that Core was t-last. I was saying that labels solve the problem. I hadn’t realized you were against labels. What is it that makes them noisy? That is not my experience at all. I find them extremely clear, and they allow currying with any argument, which is extremely powerful and elegant, and which I end up using a lot.

That said, this thread isn’t a discussion of Belt’s relative merits. Belt has it’s own priorities and they do not seem to be the same as mine. I believe that if Belt supported labels there would be more reasons to use it, currently my feelings using it are similar to when I used PHP - it feels inconsistent and frustrating. However, there are larger issues that I’m sure you’re considering.


#16

I tend to agree with @pbiggar on labels. Jane Street Base uses labels extensively. It labels both ~init and ~f for all its folds, even though the types are obviously different. It labels ~f for its maps too, even though it’s the only argument besides t.

Labels make your code more readable at a glance. And Jane Street write the library for themselves, first and foremost, and they are in the position to hire the best of the best. Yet they prefer to be explicit and reduce the mental load of their brilliant devs. Reason, on its part, is supposed to be widely adopted, by developers of wildly varying skills level that also have to learn styles, browser quirks, and whatnot, so I think readability is way more important here. Terseness, less so.

BTW, I guess the article you link to is pretty old because Yaron writes:

Sadly, this often conflicts with the most useful order for partial application.

And labeling everything solves that problem too. I’ve yet to run into problems with partially applied functions from JSB, whether I pipe them or pass them to higher order functions.

Also, only label arguments of the same type is a pretty straightforward rule, but still not as straightforward as label everything but t. So the latter would probably make for a more uniform interface.

But that’s not as important as the fact you can see what parameters mean at a glance.


#17

Back to the original question, has anyone had success using something like Base with bucklescript? It seems like it might be possible to do with bsb-native, but only with native builds.

There’s a really old issue thread on the official Bucklescript repo about whether it’s possible to easily include native libs, and so far it hasn’t presented a better way than just manually building it with bsc and then publishing on npm.


#18

Has anyone had any luck with https://github.com/darklang/tablecloth?

It works in both the native and bucklescript ecosystems, wrapping janestreet/Base in native and wrapping Belt in bucklescript.


#19

I wrote that lib in response to the lack of solid answers in this thread. We’re using it on our frontend, but haven’t converted the backend yet. So far so good, but it’s very early, there’s still a lot of functions missing, and some with questionable names or definitions. I’d love people to try it and open issues with feedback!


#20

Looks sweet! A few questions regarding web and Belt.

  1. How big is the overhead, performance-wise and size-wise? Do the bindings result in a lot of extra JS? I mean, one of Belt’s perks is how small and close-to-js it is.
  2. How do you plan to deal with missing functionality in Belt? Include it in Tablecloth? Contribute to Belt?
  3. How do you plan to deal with changes in Belt? Belt seems pretty undermanned and raw at the moment, so is probably subject to change.
  4. If one uses Tablecloth-over-Belt, would you rather recommend contributing to Belt or to Tablecloth?

#21

So there are two answers to that, one is “in the future, as designed” and one is “right now, in alpha version”.

  1. a. In the future: There should be almost no overhead - they’re almost all simple wrapper functions, and the compiler should be able to optimize them all to nothing (I dont know if that is in fact the case though). We deliberately wrap the platform-native types, as those library writers have spent time on this.
    b. Now: Most of the code is really quickly written, hacked together, and will not do well in a benchmark. If you look at the code, you’ll see we often convert something to a list and back to an array to use whatever functions Belt had available. However, while they could certainly be improved, we haven’t seen any slowdown in our app. We will certainly be improving them, and welcome contributions to help with this.
  2. The goal of Tablecloth is to not have to think about the differences between Belt, Core, Base, and other libs you app may use like containers, batteries, etc). If we need new high-level functionality and it isn’t in Belt, we should add it to tablecloth. If some functions we’re building already exist in Belt, use them. I haven’t got a sense of what it’s like to contribute to Belt, as my goals were different than Belt’s.
  3. I haven’t seen this happen in practice, and I don’t have a plan here.
  4. Por que no los dos?

#22

I missed this conversation the first time around.

I am a bit confused about t-first/t-last argument since Base also adopts t-first convention

I have started casually referring to JaneStreet’s convention as “t-middle” (that term is not endorsed by anyone I just needed a way to refer to it). It seems they are pretty consistent with putting the positional arguments in the middle, with the optional named arguments on the left, and the non-optional named arguments on the right.

In addition to that convention, Base/Core seem to make very heavy use of labeled arguments and typically only one positional argument - though it’s not always the case. In the event that a function takes two positional arguments I believe the “main” positional argument would come first. (Can you find a counter example?) It’s rare that you have two non-named arguments though.

This convention has some nice properties, such as removing the problem with optional named arguments, and typically avoids ever having to include a unit argument to “fill in the defaults” for the optional arguments.


#23

Thanks, I hadn’t heard of this before and its exactly what I was looking for.

After getting to grip’s with Base/Core I really, really like its consistency and the way it uses labelled arguments. Its definitely something I would love to see more of in reason-land.