On Saturday, March 23, 2024 1:30:29 PM MDT Menjanahary R. R. via Digitalmars-
d-learn wrote:
> The next code works as is but ...
>
> ```
> import std.stdio;
> import std.traits;
>
> bool isPrime(T)(T n) if (isIntegral!T) {
>      if (n <= T(3)) return n > T(1);
>
>      if (n % T(2) == T(0) || n % T(3) == T(0)) return false;
>
>      for (T candidate = T(5); candidate * candidate <= n;
> candidate += T(6)) {
>          if (n % candidate == T(0) || n % (candidate + T(2)) ==
> T(0)) {
>              return false;
>          }
>      }
>
>      return true;
> }
>
> T nextPrime(T)(T n) if (isIntegral!T) {
>      if (n < T(2))
>          return T(2);
>
>      T candidate = (n % T(2) == T(0)) ? n + T(1) : n + T(2); //
> Start from next Odd
>
>      for (;; candidate += T(2)) { // Skip even
>          if (isPrime(candidate)) {
>              return candidate;
>          }
>      }
> }
>
> void main() {
>      int num = 10; // Example starting number
>      writeln("\nNext prime after ", num, " is ", nextPrime(num));
> }
> ```
>
> ... it doesn't at all once I change `int num = 10;` to `const int
> num = 10;`. I'm confused
>
> How to fix it?

Well, when nextPrime is instantiated, the type of T is inferred from the
function argument. So, if num is int, then T is int, whereas if num is
const int, then T is const int. The same with isPrime.

And so, when you declare candidate to be T, it'll be const int when num is
const int, in which case, you can't use += on it, because that would mutate
it.

One way to fix this would be to do something like

    alias U = Unconst!T;

at the top of each of your functions (Unconst is from std.traits and removes
const, inout, and immutable from the type), and then instead of using T
within the function, you use U - though if you want to return a mutable
value, then you'd want to change nextPrime to return auto instead of T.

Another approach that does basically the same thing would be to change the
function signatures to

    bool isPrime(T : const U, U)(T n) if (isIntegral!T)

    T nextPrime(T : const U, U)(T n) if (isIntegral!T)

https://dlang.org/spec/template.html#argument_deduction in the spec talk
about that trick a bit, but it's basically a way to declare a second
template parameter based on the first one. It's more obtuse if you're not
used to reading that sort of thing, but it infers U based on the fact that T
has to be implicitly convertible to const U, which in effect means that U
is mutable whether T was mutable, const, immutable, or inout. Either way,
isIntegral!T still restricts T to be an integral type. Ultimately, the
effect is the same as declaring

    alias U = Unconst!T;

inside the function, but it's then part of the function signature, and you
didn't need to instantiate Unconst. And of course, like the first solution,
you would need to change the internals of those functions to use U instead
of T. So, which you go with is a matter of personal preference.

On the other hand, if you don't absolutely need to retain the original type,
an even simpler solution is to just use long.

    bool isPrime(long n)

    long nextPrime(long n)

since then any of the built-in integral types will work with it - but of
course, the result is then long, not int or whatever it is that you passed
in.

Regardless, the key thing to understand here is that when templated
functions infer the types of their arguments, any qualifiers on the type are
normally left on the type. The one exception is dynamic arrays. They're
inferred as having the type you get when slicing them - which is tail-const.
E.G.

immutable string s;
static assert(typeof(s[]) == string);

or

const(int[]) arr;
static assert(typeof(arr[]) == const(int)[]);

For better or worse, arrays do that to avoid requiring that you explicitly
slice them to pass them to range-based functions in the cases where you did
something like declare a string to be immutable. But nothing like that
happens with any other types, so if you pass a const Foo - or a const int -
to a templated function, it's instantiated with that exact type.

- Jonathan M Davis



Reply via email to