Trouble with recursive method

I’m working on the Collatz exercise. Tests pass with a looping solution:

module CollatzConjecture
  def self.steps(input : Number) : Number
    raise ArgumentError.new if input <= 0
    step = 0
    until input == 1
      input = input.even? ? (input // 2) : (3 * input + 1)
      step += 1
    end
    step
  end
end
$ crystal spec
......

Finished in 319 microseconds
6 examples, 0 failures, 0 errors, 0 pending

But trying it recursively:

module CollatzConjecture
  def self.steps(input : Number, count = 0) : Number
    raise ArgumentError.new if input <= 0
    case
      when input == 1 then count
      when input.even? then steps(input // 2, count + 1)
      else steps(3 * input + 1, count + 1)
    end
  end
end

and crystal fails with

$ crystal spec
Showing last frame. Use --error-trace for full trace.

There was a problem expanding macro 'macro_140506330720336'

Code in /usr/share/crystal/src/int.cr:185:5

 185 | {% begin %}
       ^
Called macro defined in /usr/share/crystal/src/int.cr:185:5

 185 | {% begin %}

Which expanded to:

 > 2 | if other == 0
 > 3 |   raise DivisionByZeroError.new
 > 4 | elsif self < 0 && self == Int::MIN && other == -1
                                 ^-------
Error: undefined constant Int::MIN

I was able to make it pass by altering the method signature, specifying a specific int type:

  def self.steps(input : Int32, count = 0) : Int32

Any thoughts?

I believe this could be a bug in crystal.

If It is okay with you I like to report this (unless you want to) but to give some background.

Crystal has 2 “runtimes”, one under compilation and one where it runs the compelled code. Under the compilation, all macros are runed, that is where all variables and argument values get assigned a type if not already assigned. In this case, is the type, the method return is Number. Number has the subgroups int and floats.

The number isn’t a real “type”, instead when writing Number so will the compiler give a more exact time when the code is compiled like int32 or float64. Also known as a macro, macros are super helpful and are a bit here and there in crystal. I plan to explain them deeper in a learning mode. But if you are interested so could I explain deeper now. But in general why you would want to say that a method returns a number because it makes the method quite flexible while still forcing it to return a numeric type.

It seems like in this case is the “type” int and the method it is calling, is calling the constant min for that int type. But the general int type doesn’t have a min

Yes please do.