T is not a subclass of Exception


#1

Hello,

I’m implementing an abstract HTTP rescuer handler:

require "http/server"

abstract class Rescuer(T)
  include HTTP::Handler

  def call(context)
    call_next(context)
  rescue error : T
    handle(error)
  end

  # Bug: https://github.com/crystal-lang/crystal/issues/6762
  def handle(error : T)
    {% raise "Must be implemented" %}
  end
end

class BasicRescuer(T) < Rescuer(T)
  def handle(error : T)
    puts error.inspect_with_backtrace
  end
end

server = HTTP::Server.new([BasicRescuer(Exception).new]) do |context|
  context.response.print("Hello\n")
end

server.bind_tcp(5000)
puts "Listening"
server.listen

Unfortunately, I’m getting this compile-time error:

in rescuer.cr:8: T is not a subclass of Exception

  rescue error : T

What am I doing wrong?


#2

It’s a bug, in the generic subclasses of Rescuer(T) there’s the non-instantiated BasicRescuer(T) and the T comes from there.

Generic inheritance doesn’t work well right now so my advice is to try to avoid it as much as possible.


#3

It actually seems to be a problem with generic types including a module. I’ll investigate but I can’t promise I’ll fix it.


#4

@vladfaust Please open a bug report.


#5

My 2cents: Using include + Module works however:

require "http/server"

module Rescuer(T)
  include HTTP::Handler

  def call(context)
    call_next(context)
  rescue error : T
    handle(error)
  end

  abstract def handle(error : T)
end

class BasicRescuer(T)
  include Rescuer(T)

  def handle(error : T)
    puts error.inspect_with_backtrace
  end
end

server = HTTP::Server.new([BasicRescuer(Exception).new]) do |context|
  context.response.print("Hello\n")
end

Anyway, I think it’s a good practice to avoid inheritance like pest. More I develop, and less I think inheritance is useful, when you have great composition tools instead (include & extend) :slight_smile:


#6

I’d be happy to get rid of inheritance, but the language has some bugs which requires it in some cases =(

For example, see Type#all_includers and Array co-something inconsistency


#7

That’s true exotic construction may sometime trigger error in the compiler.

However on your two examples, no offense but I think you try to bend Crystal where you should not.

Crystal is not a language offering reflection at runtime like Java or Ruby. And it would / should not anyway. Storing classes objects for example sounds like an anti-pattern for me. Because basically in Crystal you won’t do anything with it. You cannot call new over a referenced class object for example. That’s why Array(MyAbstractClass.class) is a non-sense, and you cannot either store Class object in variable in Crystal while it this can be useful in Java for example.

If you need to instantiate dynamically some objects using sort-of reflection, you must implement the factory pattern or use compile time linking with name matching to create and manage your link between the code and the Classes.
I would be happy to give you some help, as I faced this kind of design maze in Clear, as there’s some kind of reflection-ish things to deal with :slight_smile: