Do you know of `StdLabels` and `MoreLabels` modules?

Do you use them? If yes, how often? Share your experience with them please.

This is related to an open issue, and is my own attempt to gather community responses to help the maintainers gauge the interest in those modules, at least at a limited empirical scale.

For the record, I rely on them only on certain functions, blit is a classic use-case, and also when it makes sense to have data first then a large trailing function on the likes of map and filter, as in Data.map data ~f:(fun ...), with the alternative sometimes being data |> Data.map (fun ...), or just have the data way down at the tail end, or give the lambda a name, which are all less appealing IMO…

What’s your experience with standard labelled modules?


bonus section for those who want to see more labels

There is a problem with having to mirror files (once with labels and once without) to produce the current state of affairs, not only for the maintenance burden in syncing up the modules, but also because it’d bloat up the distribution and I imagine that bloating alone annoyed enough end-users for it to motivate maintainers to explore compression among–other–things.
Additionally, labels complicate partial-application and interact with polymorphism in surprising ways (try to misspell a label!), and they just add too much verbosity sometimes… There are true and tangible reasons to reject a labels take-over in standard APIs for many OCaml users including myself. And in that sense, label-heavy APIs like base/core feel a bit too much and a lot too much to make mandatory in stdlib (not that I think it’ll ever happen with stdlib’s strong backwards compat guarantees).

Now, in trying to think up a way to relieve this situation, the first question that popped up in my head was “why not unify?”, and I don’t mean this in a user-facing way, I mean it in a toolchain way. It’d be a lot less work in the long term to maintain only the labelled versions, at the cost of having to answer now the difficult question of how to do that but keep them optional in a user-facing way. Perhaps give the compiler the facility to discard labels completely (not just ignore them with -nolabels)…? Perhaps as an annotation on module aliases/definitions/inclusions or as a flag that’s passed in the commandline…? Perhaps with the aforementioned default inverted (i.e. annotate to get labels)…?

The immediate problem I see with my ideas is that now labels become a take-it-or-leave-it deal, and we have labels in plain stdlib (e.g. String.starts_with)… I don’t know if either “it’s ok because they’re still in stdlabels” or “let’s come up with a way to override the unlabel annotation/flag”, is a satisfactory answer to that, really.

This is an invitation to brainstorm this problem, for those interested, on top of answering the main questions above.

1 Like

The problem is that you will find it difficult to make everyone agree – for one I’m against pointless labelling like the function argument of List.map even more so now that upstream decided to enable the warning about unlabelled use of labelled argument by default.

Labels go against the grain of functional programming; they make type compatible functions incompatible because of the labels name. This prevents simple function reuse (example) and makes many elegant functional programming technique where you lift functions into another domain horrible to use.

Personally I think it’s a design sin to have both labelled and unlabelled versions of the stdlib. For some time I tried to advocate to deprecate the labelled version, see the issue for the rationale. But I never managed to gather upstream support for the idea so I eventually gave up.

So I would say that the current status quo which basically freezes the existing set of labelled modules and where labels are simply used with good rationale and taste in new developments of the “unlabelled” stdlib is probably not worth disturbing.

3 Likes

I know of stdlabels and morelabels but I never use them (unless contributing to a project that already uses them). I do appreciate labels for functions which can be very confusing (Ă  la blit).

Another problem with labelled arguments: They make backwards compatibility worse.

With labels, names become parts of API and you can’t safely rename an argument anymore without breaking some external code. This means that labels may introduce undesirable tight coupling.

Thus being said, I still think that labels have their place. But introduction of a new labels should be done with care.

Same here: I know about the labeled modules but I don’t use them. Except that I somehow don’t have to think to use blit :stuck_out_tongue:

I particularly dislike the idea of ~f for List.map because it would force me to use parentheses. I wouldn’t be able to write Fun.flip List.map list @@ fun x -> if the function was labeled. (Although in practice I tend to just redefine the order of arguments for such functions.)

I do use labels a lot when it makes sense though.

That’s not true, the label does not have to match your variable name:

let foo ~bar:baz = baz

But yeah, I do care about labels because they play fairly well with the |> operator and are extremely useful for functions where the arguments are all of the same type.

That said I don’t particularly care about the labels in the standard library as the libraries I use most (Base/Core/Stdune) define their own interfaces with labels. Looking at the PR, SeqLabels to mirror ListLabels seems a fairly uncontroversial addition to me.

4 Likes

Ah, nice to know about the ability to rename labels locally!

Still, I believe that by introducing another dimension to your API, you increase the potential area for breakage.

Same as @Leonidas , in the rare cases where I cannot use Jane St’s Base I stick to the conventions of the project even if it uses hard-to-read/hard-to-modify unlabelled APIs and/or I call directly individual modules e.g. ListLabels.map instead of StdLabels.