module Foo
@@foo : Bool?
def foo?
@@foo ||= true
end
end
class Bar
extend Foo
end
Bar.foo?
Maybe I don't fully understand how class variables work at module level but it seems to me that above code should work, instead it fails with error:
Error in line 13: instantiating 'Bar:Class#foo?()'
in line 5: Can't infer the type of class variable '@@foo' of Bar
The type of a class variable, if not declared explicitly with
`@@foo : Type`, is inferred from assignments to it across
the whole program.
The assignments must look like this:
1. `@@foo = 1` (or other literals), inferred to the literal's type
2. `@@foo = Type.new`, type is inferred to be Type
3. `@@foo = Type.method`, where `method` has a return type
annotation, type is inferred from it
4. `@@foo = arg`, with 'arg' being a method argument with a
type restriction 'Type', type is inferred to be Type
5. `@@foo = arg`, with 'arg' being a method argument with a
default value, type is inferred using rules 1, 2 and 3 from it
6. `@@foo = uninitialized Type`, type is inferred to be Type
7. `@@foo = LibSome.func`, and `LibSome` is a `lib`, type
is inferred from that fun.
8. `LibSome.func(out @@foo)`, and `LibSome` is a `lib`, type
is inferred from that fun argument.
Other assignments have no effect on its type.
Can't infer the type of class variable '@@foo' of Bar
Might be related to #4039?
I think extend
is a poor language feature. We should probably drop it and only allow inclusion via include
, which works as multiple-inheritance.
What do mean? Having include
behave has both include
+ extend
?
@asterite removing extend
will break some usage for doing interfaces directly with classes (and not instances):
http://stackoverflow.com/a/42053962/117298
(I mean until Interface.class
gets implemented)
I would like to know more why extend
is poor from your perspective.
Thank you.
I think include + extend in Ruby aren't very intuitive. For example:
module Moo
def self.moo
end
def moo
end
end
class Foo
include Moo
end
p Foo.new.moo # OK
p Foo.moo # Error
I understand why the second line is an error, but when I use include
I generally want to include all functionality from a type inside another type. That includes both instance and class methods.
I personally never use extend
in Ruby unless I want to make all methods from a module be prefixed with def self.
instead of writing that. But if that's the only reason extend
exists then I'd like to remove it. You'll have to write def self.
a bit more, but it's less things to know and learn, and a simpler language.
I think the issue mentioned in stack overflow can still be implemented, I'm not sure it's related to include vs. extend.
Thank you @asterite for your response, indeed include
and extend
sometimes presents some confusion, specially when coming from a non-Ruby background.
This decision will require solve things like abstract def self.something
which are currently not possible (but extend
does work for that).
Merging both concepts will definitely reduce that confusion area. Looking forward read the proposal.
Cheers.
abstract def self.something
is impossible to implement, because classes always exist, a user can't explicitly instantiate them. That's why using classes as interfaces will probably never happen. What I mean is this:
module Moo
abstract def self.moo
end
# Nothing prevents me from doing this:
Moo.moo
So abstract class methods probably don't make sense. In fact in no statically typed language there's such concept, only abstract instance methods.
abstract def self.something
is impossible to implement
I hear you, valid points and something that needs to be solved since can be done right now:
module Interface
module ClassMethods
abstract def call
end
macro included
extend ClassMethods
end
end
class Action
include Interface
# uncomment this for compile to succeed
# def self.call
# end
end
Error in 22.cr:3: abstract `def Interface::ClassMethods#call()` must be implemented by Action:Class
abstract def call
^~~~
I have a few other cases where include
and abstract
don't play nice with each other, but will save them for a later trolling :smiley_cat:
For reference: This has come up in another question on Stack Overflow: https://stackoverflow.com/questions/45631130/defining-class-vars-in-modules-in-crystal-causes-error
I had a similar issue, wanted to be able to call a method referencing a @@
variable from a class / module by including or extending a module.
A workaround that makes the compiler happy: in the parent module, define a method on self
, then define an instance method calling it
module Foo
@@foo : Bool?
def foo?
Foo.foo?
end
def self.foo?
@@foo ||= true
end
end
class Bar
extend Foo
end
Bar.foo?
Most helpful comment
I think include + extend in Ruby aren't very intuitive. For example:
I understand why the second line is an error, but when I use
include
I generally want to include all functionality from a type inside another type. That includes both instance and class methods.I personally never use
extend
in Ruby unless I want to make all methods from a module be prefixed withdef self.
instead of writing that. But if that's the only reasonextend
exists then I'd like to remove it. You'll have to writedef self.
a bit more, but it's less things to know and learn, and a simpler language.I think the issue mentioned in stack overflow can still be implemented, I'm not sure it's related to include vs. extend.