Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Class inheritance from Array(T) with YAML.mapping in T #3238

Closed
AlexWayfer opened this issue Sep 3, 2016 · 4 comments
Closed

Class inheritance from Array(T) with YAML.mapping in T #3238

AlexWayfer opened this issue Sep 3, 2016 · 4 comments
Labels
kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:compiler
Milestone

Comments

@AlexWayfer
Copy link
Contributor

Hello!

I have a code:

require "yaml"

class Child
  YAML.mapping(
    name: String,
    age: Int32
  )
end

class Children < Array(Child)
end

yaml = <<-END
- name: Alex
  age: 6
- name: Jessy
  age: 8
END

Children.from_yaml yaml

And I have a problem:

Error in line 20: instantiating 'Children:Class#from_yaml(String)'

in ./src/yaml/from_yaml.cr:3: instantiating 'YAML::PullParser#read_stream()'

  parser.read_stream do
         ^~~~~~~~~~~

in ./src/yaml/from_yaml.cr:3: instantiating 'YAML::PullParser#read_stream()'

  parser.read_stream do
         ^~~~~~~~~~~

in ./src/yaml/from_yaml.cr:4: instantiating 'YAML::PullParser#read_document()'

    parser.read_document do
           ^~~~~~~~~~~~~

in ./src/yaml/from_yaml.cr:4: instantiating 'YAML::PullParser#read_document()'

    parser.read_document do
           ^~~~~~~~~~~~~

in ./src/yaml/from_yaml.cr:5: no overload matches 'Children.new' with type YAML::PullParser
Overloads are:
 - Children.new(size : Int, value : T)
 - Children.new(initial_capacity : Int)
 - Children.new()

      new parser
      ^~~

How I can do that Children class?

Reproducible on master branch.

Crystal 0.18.7 (2016-07-04) on Arch Linux.

Thanks.

@asterite asterite added kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:compiler labels Sep 3, 2016
@asterite
Copy link
Member

asterite commented Sep 3, 2016

More generally, this works:

class Foo
  def initialize(x : Int32)
  end

  def self.new(x : Char)
  end
end

class Bar < Foo
end

Bar.new('a')

But this doesn't:

class Foo(T)
  def initialize(x : Int32)
  end

  def self.new(x : Char)
  end
end

class Bar < Foo(Int32)
end

Bar.new('a')

Something to do with generic classes.

As a workaround, you can use this:

class Children < Array(Child)
  def self.new(pull : YAML::PullParser)
    super
  end

  def self.new(pull : YAML::PullParser)
    super do |elem|
      yield elem
    end
  end
end

Alternatively, just using alias Children = Array(Child) should be enough. I personally consider subclassing containers like Array and Hash a bad practice or a bad design, and would try to use composition instead of inheritance in these cases.

@AlexWayfer
Copy link
Contributor Author

Alternatively, just using alias Children = Array(Child) should be enough.

I want to add method like concat for Children class, where Child will be compare by one attribute and update from another Children:

children # => [#<Child @name="Rob", @age=8>, #<Child @name="Alice", @town="New York", @age=7>]
other_children # => [#<Child @name="Alice", @age=9>, #<Child @name="John", @age=6>]
# some magic method
children | other_children # => [#<Child @name="Rob", @age=8>, #<Child @name="Alice", @town="New York", @age=9>, #<Child @name="John", @age=6>]

and would try to use composition instead of inheritance in these cases

What do you mean?

@asterite
Copy link
Member

asterite commented Sep 3, 2016

I mean that you are defining a | method that basically has nothing to do with what Array#| does. Why not simply use Array(Child) and define, somewhere, a merge_children methods that does the operation? Less classes around, more explicit names, etc.

I would always think twice (or maybe many more times) before subclassing a generic type, specially when subclassing containers.

@AlexWayfer
Copy link
Contributor Author

| is just example.

merge_children is FP, not OOP :) It's accessible, but is not so beautiful (for me).

Okay, thanks.

@asterite asterite added this to the 0.19.1 milestone Sep 3, 2016
jackturnbull added a commit to jackturnbull/action-controller that referenced this issue Sep 2, 2019
Currently seeing a compiler bug when attempting to compile with raven.cr. Although I'm not convinced entirely that this is an issue with action-controller, it does seem to be considered good practice to stick with composition rather than inheritence for this sort of stuff (crystal-lang/crystal#3238 (comment)).

The approach taken was effectively the same as amberframework/amber#930 which fixed the same issue within Amber.
jackturnbull added a commit to jackturnbull/action-controller that referenced this issue Sep 2, 2019
Currently seeing a compiler bug when attempting to compile with raven.cr. Although I'm not convinced entirely that this is an issue with action-controller, it does seem to be considered good practice to stick with composition rather than inheritence for this sort of stuff (crystal-lang/crystal#3238 (comment)).

The approach taken was effectively the same as amberframework/amber#930 which fixed the same issue within Amber.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:compiler
Projects
None yet
Development

No branches or pull requests

2 participants