Let’s say I have the enums Foo and Bar
enum Foo : Int32
# a lot of options
end
enum Bar : Int64
# a lot of options
end
And for Int32 and Int64 I have defined:
struct Int32
def self.foo (x)
1
end
end
struct Int64
def self.foo (x)
5
end
end
Now I would like to be able to do something like:
foo= Foo.foo x
With the intention: Foo’s values are of type Int32, so I would want that somehow Int32.foo(x)
gets called and if I would use Bar.foo(x)
then Int64.foo(x)
shall be called, and so on.
I guess I would have to define a method self.foo
for struct Enum
but I wouldn’t know how to refer to the enum’s value type in such a method
struct Enum
def self.foo (x)
__something_to_refer_to_the_type_of_the_value_of_the_enum__.foo x
# basically this: new(0).value.class.foo x
# but I assume there is a better way than the new(0) workaround
end
end
With what would I have to replace __something_to_refer_to_the_type_of_the_value_of_the_enum__
?
This doesn’t answer your question, but adding methods to core types is not recommended. I mean, you can do it. But I don’t see any strong reason to do things like that.
1 Like
I’m a bit confused on the purpose this whole thing is serving? Like is that idea that calling Int32.foo 10
returns a Foo
instance with a value of 10
?
1 Like
It looks like you want the #value
method to get the integer value out of the enum instance.
enum AAA : Int32
Aaa
Bbb
Ccc
def foo
value.foo
end
end
struct Int32
def foo
"foo#{self}"
end
end
p AAA::Aaa.foo # => "foo0"
p AAA::Bbb.foo # => "foo1"
p AAA::Ccc.foo # => "foo2"
But as @asterite said, avoid adding new methods to built-in types, especially primitive types. That will cause issues down the line.
1 Like
I just simplified it a bit in my first example code, as it seemed to make the code just unnecessary complicated for what 'm trying to ask.
What I try to do achieve is the same as this (working) code would do:
struct Enum
def self.foo (x)
from_value values[0].value.class.foo x
# x is not an integer at all, but
# Int32.foo(x), Int64.foo(x), etc will return values of Int32, Int64, etc
end
end
The workaround values[0].value.class
works, but I would imagine there must be some way, which would be more straightforward, to address the enum type’s value’s type.
Thanks. But sadly it’s not what I try to achieve (which would happen already before there is even an instance of AAA, basically I need it to figure out the value AAA shall get initiated with).
I originally used some pseudo enums (custom classes, which did behave somewhat like enums). But I’m trying to replace it with real enums (as Crystal’s enums seem to be quite awesome). And while new methods for built-in types might be to avoid, I guess they will be preferable over my pseudo enums which I used in the first place.
No, I get what you’re wanting to do, but it’s still not clear to me why. Like do your enum’s value actually represent something? If not then I’m doubtful that’s what you actually want to use. Just feels weird to go from a number, call an enum class method, that calls a monkey patched class method on specific number types, just to get another number? Could you share a bit more on what you’re using this for? May be a simpler way to go about it.
But to answer your question, pretty sure you could just do typeof(x).foo x
. E.g.
struct Enum
def self.foo(x)
pp typeof(x).foo x
end
end
Enum.foo 10
Enum.foo 10_i64
1 Like
The x in my example is most of the time a socket (and sometimes a buffer and in rare occasions a pseudo socket which just holds an infinite amount of ‘0’ bytes). The integers I retrieve from x are (most of the time) 7bit encoded, but some are just “normal” (little endian with the length of the type), and to deal with them I have the deserialisation done via TargetType.foo(x)
(or actually from
instead of foo
, and I’m fully happy with the resulting code, like in Int64.from host.socket
).
Some fo the transferred integers represent enums, and I end up wanting to do @someEnum= SomeEnum.from socket
; of course I could do @someEnum= SomeEnum.from_value Int64.from socket
but this would mean I would repeat the type Int64 even though it’s already known that enum SomeEnum : Int64
. And as the enum is (and needs to be) typed already I want to avoid to type it anywhere else again.
Okay cool, this helps a lot I think. Because you know that each enum should handle a specific integer type. I think you could do something like:
Enum SomeEnum
def self.from(socket) : self
self.from_value Int64.from socket
end
end
Then you can just do SomeEnum.from socket
and call it a day? Although this of course would not handle a more generic API of defining something on Enum
itself that handles returning one or the other enum types based on the type it’s defined as.
There was a PR to add Enum.base_type
: Document how to change base type of an enum by Blacksmoke16 · Pull Request #9803 · crystal-lang/crystal · GitHub. But wasn’t really worth adding. Although you could leverage some compiler typeof magic to do like:
struct Enum
def self.from(socket)
self.from_value typeof(self.values.first.value).from socket
end
end
This would return the type of the enum via some magic that doesn’t actually result in an array access or anything.
1 Like
Your suggestion for typeof magic, looks almost exactly like my current workaround.
The only real difference is that my workaround uses
values.first.value.class
instead of
typeof(values.first.value)
Is there in this specific context one preferable over the other?
Yes, Carcin illustrates it well. When you use values.first.value.class
that’s actually calling those methods and creating an array and such. Whereas typeof(values.first.value)
does not actually do any of that. I.e. which is why first
is only printed once in the playground link.
2 Likes
whereas typeof(values.first.value)
does not actually do any of that
this I didn’t know at all. I’m glad I asked (and you replied). Thanks!