crystal lang : in case of a Class as a field

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



crystal lang : in case of a Class as a field



I'm just writting an Exception, which should stores a Class object as a field for the error message process.


Class


class BadType < Exception
getter should_be : Class
getter actual : Class
end
def feed(pet : Animal, food : Food)
raise BadType.new should_be: Cat, actual: pet.class if food == "fish" && !pet.is_a?(Cat)
end



But, Class is abstract so cannot be used here as a field type.


Class



So, how to solve this in my case? I hadn't found any derived class of Class, so does it mean one can never store a Class object as a field? But here my problem is a meaningful usage for this (any type check depends on input may require this BadType).


Class


Class


BadType



I'm not sure whether I missed something, so I came here first.




2 Answers
2



Class can't (yet) be used as a ivar type. Maybe it never will, I don't know.


Class



But you can actually use generics for concrete data types, inferred from the arguments to the constructor:


# possible workaround
class Foo(T, U)
def initialize(@bar : T, @baz : U)
end
end

Foo.new String, Int32



I don't know your exact use case, but chances are you don't really need these values as classes. You probably can't do much with it anyway and drawing from your example I guess it's mostly for showing debugging information.



So it is very likely that just storing the names of the classes (as strings) would be a better solution for this problem.


# better solution
class Foo
@bar : String
@baz : String
def initialize(bar : Class, baz : Class)
@bar = bar.name
@baz = baz.name
end
end

Foo.new String, Int3



The generic arguments mean a new concrete type is created for every combination of classes used with Foo. That can have an impact on compiler performance.


Foo



I would most definitely use strings for this. Even if you need the classes for some particularly processing later on, it's probably better to just map the strings to the constants using a macro-generated lookup table.



Try generics:


class BadType(ClassA, ClassB) < Exception
getter should_be : ClassA
getter actual : ClassB

def initialize(@should_be, @actual)
@message = "Bad type: should be #@should_be, actual is #@actual"
end
end

def feed(pet : Animal, food : Food)
raise BadType(Animal.class, Animal.class).new should_be: Cat, actual: pet.class if food == "fish" && !pet.is_a?(Cat)
end

class Animal
end

record Food, food : String do
def ==(other_food)
@food == other_food
end
end

class Cat < Animal
end

class Dog < Animal
end

feed pet: Dog.new, food: Food.new("fish")



Output:


Unhandled exception: Bad type: should be Cat, actual is Dog (BadType(Animal:Class, Animal:Class))
from /eval:11:3 in 'feed'
from /eval:29:1 in '__crystal_main'
from /usr/lib/crystal/crystal/main.cr:104:5 in 'main_user_code'
from /usr/lib/crystal/crystal/main.cr:93:7 in 'main'
from /usr/lib/crystal/crystal/main.cr:133:3 in 'main'
from __libc_start_main
from _start
from ???



Demo: https://carc.in/#/r/4pgs






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard