Search code examples
castingcrystal-lang

Type specification in Crystal


I'm new to Crystal and i'm trying to translate the following program to Crystal:

#include <stdio.h>

long rev(long n) {
  long m = 0;
  while (n > 0) {
    m = 10*m + n%10;
    n = n/10;
  }
  return(m);
}

int main() {
  for(int n=1; n<=10000000; n++) {
    long m = n*rev(n);
    if (m==rev(m))
      printf("%d\n", n);
  }
}

I wrote the Crystal program

def rev(n : UInt64) : UInt64
  m : UInt64 = 0
  while n > 0
    m = 10_u64*m + n%10_u64
    n = n//10
  end
  m
end

(1_u64 .. 10_000_000).each { |n|
  m = n*rev(n)
  if m == rev(m)
    print(n,"\n")
  end
}

I found odd that, in order to compile, at some points I had to specify the type of the literals (n%10_u64) but in others don't (n//10). I'm I missing something or just Crystal is not consistent about casting?

Also, I like to know, is this a good translation of the c program or how an experienced Crystal programmer would do it?


Solution

  • The default number type in Crystal is Int32. So if you don't specify a type, it's that. Your algorithm operates on UInt64 so you need to take some extra steps.

    Ideally, you would use only UInt64 numbers in your algorithm, so everything would be typed the same. But you don't actually need to because the methods casts operands to fit. The return type is always the type of the first operand. UInt64#//(Int32) returns UInt64 and Int32#*(UInt64) returns Int32. However, there is no automatic promotion like in C.

    n // 10 is fine, it returns the type of n which is what you want. But 10 * m is bad because it returns the default type of 10 which is Int32 and assigning it to n changes its type.

    This might seem unexpected at first, but the reason for returning the first type is to allow assignment operators like x += y to work, which expands to x = x + y. If x + y returns a different type from x, this would unintentionally influence the type of the variable.

    My advise is to explicitly type all number literals that are part of your algorithm as UInt64 to make sure that they fit together.