roc-lang / roc

A fast, friendly, functional language.
https://roc-lang.org
Universal Permissive License v1.0
4.12k stars 290 forks source link

`Num.clamp : Num a, Num bound, Num bound -> Num a` #2439

Open JanCVanB opened 2 years ago

JanCVanB commented 2 years ago

Should this be a builtin?

Inspired by C++ std::clamp, #2411, and the imagined verbosity of implementing this in pure Roc.

Examples:

Num.clamp 3 4 6 == 4
Num.clamp 5 4 6 == 5
Num.clamp 7 4 6 == 6

Num.clamp Num.maxU16 Num.minI8 Num.maxI8 == (Num.maxI8 |> Num.toU16)
rtfeldman commented 2 years ago

Makes sense to me! 👍

So I assume the type would be clamp : Num a, Num a, Num a -> Num a yeah?

JanCVanB commented 2 years ago

Actually, take a look at that second example:

Num.clamp Num.maxU16 Num.minI8 Num.maxI8 == (Num.maxI8 |> Num.toU16)

I'm hoping this function can help #2411 by clamping one type to another's bounds before casting it:

toI8Clamped = \n -> n |> Num.clamp Num.minI8 Num.maxI8 |> Num.toI8
toI8Clamped Num.maxU16 == Num.maxI8

However, if all the types are the same, then a signed minimum bound couldn't be used to clamp an unsigned input, since it's unrepresentable in the unsigned type:

toI8ClampedUgly = \n -> n |> Num.clamp 0 (Num.maxI8 |> Num.toU16) |> Num.toI8
toI8ClampedUgly Num.maxU16 == (Num.maxI8 |> Num.toU16)

Right?

ayazhafiz commented 2 years ago

I would prefer clamp to take a single type parameter as suggested in https://github.com/rtfeldman/roc/issues/2439#issuecomment-1027959773, otherwise it feels a little bit too magical IMO