unicode-org / message-format-wg

Developing a standard for localizable message strings
Other
209 stars 32 forks source link

Spec language on eager vs. lazy evaluation should be clarified #784

Open catamorphism opened 1 month ago

catamorphism commented 1 month ago

In #753 , @echeran brought up the example of a custom function getSystemTimeInMillisNow() that would return the current time.

In the spec intro we currently have the language:

"This specification does not require either eager or lazy expression resolution of message parts; do not construe any requirement in this document as requiring either."

Consider the following example (which formats to a string with the current time repeated twice... or does it?):

.local $x = {:getSystemTimeInMillisNow}
{{{$x} {$x}}}

In an eager implementation, this is guaranteed to print the same time twice.

In a lazy call-by-need implementation, where the right-hand side of $x is guaranteed to be evaluated at most once, it's also guaranteed to print the same time twice.

In a lazy call-by-name implementation, it may print out two different times.

In general, custom functions might have side effects observable by MF2, and getting the current time is a reasonable example of one. That means one can't assume that call-by-need and call-by-name are going to be equivalent.

I think all that's needed is an editorial note pointing out that depending on the contents of the custom function registry, call-by-need and call-by-name may be observably different and the implementor should think about which one is the least surprising.

echeran commented 1 month ago

One thought about what to say to users in the spec text: it's not just the laziness that users should consider from side-effecting functions (which is more about exactly when to evaluate), but also whether they want to memoize those functions (that is, cache the function result per each distinct set of input given), which would be more about giving consistent output across invocations of such a function.

Also thanks to @catamorphism since this well written issue follows from his comment over on #753:

This is indeed an important point, and not just an implementation detail IMO. ... maybe it should be noted somewhere in the spec that if custom functions with observable side effects exist, then careful thought should be put into lazy implementations.

catamorphism commented 1 month ago

One thought about what to say to users in the spec text: it's not just the laziness that users should consider from side-effecting functions (which is more about exactly when to evaluate), but also whether they want to memoize those functions (that is, cache the function result per each distinct set of input given), which would be more about giving consistent output across invocations of such a function.

That's right -- there are two different ways to implement laziness, one by using memoization (aka call-by-need) and one without, and the thing to explain is that the difference between the two is actually observable from a message, if certain custom function implementations exist.