Infix operator migration

Hi

Don’t panic, this is not a debate about whether infix operators are a good thing (I hope).

Background:

My old Reason code does this sort of thing quite a bit (QQ here is a thin wrapper around zarith’s Q.Int rationals, and the %- operator is subtraction of those rationals)

      let pxHPos =
        QQ.to_float(hPos %- Data.initialScheduleValue)
        *. float_of_int(pixelsPerRepeat);

This code was written as a spike to experiment with some UI, I picked the first way to write it that seemed to work, and I expect I won’t use any of it in production code – though I will be wanting to use rationals and then converting them to float pixel values in CSS. So while I’m very much interested to hear how people think one should write arithmetic involving rationals and floats (maybe people here would advocate more strictly separating the two and just never having both in one expression, and defining and opening the standard arithmetic operators for use in rational expressions?), that’s not my focus in this post.

Of course the migration to ReScript syntax worked. However, it turns this for example (/%/ here is just Q.Int.of_ints and %+ is Q.Int.add):

QQ.to_float(i /%/ 1 %+ Data.initialScheduleValue)
|> Format.sprintf("%.2f"),

into:

QQ.to_float(\"%+"(\"/%/"(i, 1), Data.initialScheduleValue)) |> Format.sprintf("%.2f"),

Which does make it unreadable.

I’d like to use this code a bit more to play with my UI. Can I do that without rewriting it all? I heard that infix operators had gone, and then that they came back again precisely for migration purposes. However, I’ve not been able to figure out how to define infix operators in the new syntax. Any clues?

I’ve randomly tried things along these lines:

let \"%+" = QQ.\"%+"
let (\"%+") = QQ.\"%+"
let (%+) = QQ.\"%+"

But however I try to use them as operators (with or without the quoting), I get syntax errors.

Hey, thanks for setting the tone =)

Just to be clear to other readers: currently all arithmetic operators still work normally in callsites. For their declaration, you’d need to use the first form, aka let \"+" = ...:

let \"+" = ...
a + b

Playground example. Don’t do this in prod please =)

The plan is for the declaration site to receive a simplified syntax without the quoting. We’ll first start with arithmetic operators. Since arithmetic operators are a safe change, we’re also thinking of not treating them as a pure migratory feature, i.e. as opposed to other potentially allowable infixes which would stay for migration only then removed later on, the new arithmetic infixes declaration syntax might be kept as a real first-class supported feature, and even special cased, for whatever neat benefits (e.g. ad-hoc polymorphism, dedicated syntax). No promises though.

Because of that, we suggest you to codemod the rational additions, subtractions and others to +, -, etc.:

let \"+" = QQ.\"%+"

Your Q.Int.of_ints should probably stay as a regular function for now; I’d suggest migrating to that automatically converted \"/%/"(i, 1) form then do a quick replacement into of_ints(i, 1). Pipe makes this more readable:

open QQ // will give you a shadowing warning. Just a demo
(i->of_ints(1) + Data.initialScheduleValue)
  ->to_float
  ->Format.sprintf("%.2f", _) // took the liberty here

Thanks, so it sounds like:

  1. When you say “arithmetic operators” you mean exactly + - / * (and maybe ** and % I guess, and I guess the corresponding float operators +. -. etc. too). The operators like %- that I’ve used to do arithmetic on rationals are not arithmetic operators in this sense, because they’re not in that set of built-in infix operators.
  2. Defining infix operators other than those is not currently possible (right? Still not 100% clear on that, sorry if I’ve missed some docs I should have been reading all along)
  3. Because of that, I do have to rewrite my migrated code if I want to do any more work on it, so that my expressions do not mix float with rational arithmetic, nor integer with rational arithmetic (not difficult I expect, but not a tiny amount of code I think… I’ll see how it comes out).
  4. Even basic overriding of (built-in) arithmetic operators you might decide to get rid of later.

I guess in principle I could choose to use the float operators for float arithmetic and the integer operators for rational arithmetic, for example. But then sometimes I’m mixing integers with rationals in one expression rather that floats and rationals, and I think it would be very confusing to sometimes use - for rationals and sometimes -. (!), so I think strict separation of expressions that do rational arithmetic is probably required given these rules.

Point 4. seems like a genuine problem for any application that would like to use rational arithmetic or anything similar.

Actually I’m still confused by this part. It sounds like currently, as a migration tool, you’re saying it IS possible to define infix operators other than + - * / etc. (fully expecting it to go away later)? That is what I was trying to do and failed to figure out.

Yes, and potentially a few more, judging by popular request. But you can define yours as arithmetic ones since the use-case is suitable for that. It does get hard when you try to mix rationals and floats though.

Defining those have always been possible. Using those in an infix way, no.

No, I’m saying that overriding arithmetic ones is a use-case we’re interested in continue supporting.