Remix.run Logo
roenxi 12 hours ago

If we're trying to solve problems with good design, use endpoint1 and endpoint2 and then the function sorts them. Having max and min is itself a bad design choice, the function doesn't need the caller to work that out. Why should the caller have to order the ends of the interval? It adds nothing but the possibility of calling the function wrong. So in this this case:

    export function clamp(value: number, endpoint1: number, endpoint2: number): number {
      return Math.min(Math.max(value, Math.min(endpoint1, endpoint2)), Math.max(endpoint1, endpoint2));
    }
vanviegen 10 hours ago | parent | next [-]

That would lead to unpleasant surprises. When calling the function from some loop and when the bounds are inclusive, it's pretty common for (correct) edge cases to exist where you'd call the function with end===start-1. The function would do the right thing by returning an empty set. You'd get duplicate/unexpected records in some cases, that may be hard to debug.

It seems like your approach is just trying to ignore programmer errors, which is rarely a good idea.

roenxi 9 hours ago | parent [-]

I have no horse in the race and would usually just implement my clamp function the way the article does. However, if the clamp function clamping a number is an unpleasant surprise, I'm not going to accept that it is the fault of the clamp function. This hypothetical loop is buggy code and should be rewritten to expect clamp to clamp.

It is a special type of madness if we're supporting a reliance on implementation specific failure modes of the clamp function when someone calls it with incoherent arguments.

RHSeeger 5 hours ago | parent [-]

> This hypothetical loop is buggy code and should be rewritten to expect clamp to clamp.

But it makes it harder for the developer to recognize that the code is buggy. More feedback to the developer allows them to write better code, with less bugs.

Your argument could be made in the same way to claim that static typing is bad; because the caller should be calling it with the right types of values in the first place.

atombender 6 hours ago | parent | prev | next [-]

This maps poorly to the mathematical concept of a closed interval [a, b], which can be written a ≤ x ≤ b for a set of x. An interval where a > b is usually a programming error.

To ensure only valid intervals are supported at the type system level, the function could perhaps be redefined as:

    function clamp(n: number, i: Interval<number>): number
Of course, you need to deal with the distinction between closed and open intervals. Clamping really only makes sense for closed ones.
whilenot-dev 11 hours ago | parent | prev [-]

So an implicit fallback, but make it explicit through good design. Haven't even thought about this as a principle, since type checking persuades me to avoid anything implicit, thank you!