The Crystal Programming Language Forum

Call a method on parent thats also defined on the child from a different method

I can’t seem to figure out a way to call a parent class’s implementation of a method from a different method and when that parent method is also redefined in the child. For example:

abstract class Foo
  def one
    "One"
  end
 
  def two
    "Two"
  end
end
 
class Bar < Foo
  def three
    one
  end
 
  def one
    "1"
  end
end
 
b = Bar.new
 
# This should print One
pp b.three

https://play.crystal-lang.org/#/r/bfrz

You can’t just use self.one as that uses the child’s implementation. ::one doesn’t work as it looks for it on the top level. super.one errors with undefined method 'three' for Object (although this does work in Java). In PHP you could do parent::one(); , which is basically the same as Java with a different keyword.

We probably should allow calling a different method on super versus just having it assume the method you want is the one you’re calling it from?

You can’t do this either in Ruby. The way to do it is to make one call a helper method, and then call that helper method from the child class.

This feels kinds hacky, but if you make Foo to be not abstract and if you don’t mind ‘escaping’ the ‘instance’ level of your Bar object, then you can do {{@type.superclass}}.new.one as noted in Carcin . You could maybe do something like {{@type.superclass}}.new(self.param_xyz).one if you want to keep some instance data, but that still seems a bit hacky.

Is there a reason this isn’t supported? Or just something that hasn’t been implemented yet?

This is definitely hacky ha. Fortunately in my case the parent method was pretty trivial, so i just copied the code from there into the child directly.

The reason is Ruby: you can’t do it in Ruby. In my mind whenever you need to do this is a small. Could you expand more on the real use case?

The gist of it is I was porting some PHP code and that’s how they had things setup. Specifically symfony/ConsoleSectionOutput.php at 5.4 · symfony/symfony · GitHub.

The parent abstract type defines write and writeln akin to our print and puts methods as well as an abstract doWrite that defers how to do the writing depending on the child. In this case the direct parent type is for stream (IO) based output which will write the data to the stream.

The type from the link above extends the stream based type overriding doWrite as it has some additional logic within it. In some cases it calls parent::doWrite("some string", false); to simply write the data, avoiding the extra logic in the child type. It also doesn’t use write or writeln as those do some additional formatting to the data and would still invoke the child’s implementation. Because the content is already in its final form it can just utilize the parent’s implementation.

Granted in this case it’s not a big deal as the parent do_write in Crystal land is just new_line ? @io.puts(message) : @io.print(message). But I don’t see why it shouldn’t be a feature just because it’s not in Ruby. We already have the super keyword. Continue to have super ... refer to the method it’s called in but also allow calling methods on it like super.do_write "foo", false.

1 Like

If there are any positional parameters in the defs, a different hack is to give them different names:

abstract class Foo
  def one(x)
    "One"
  end
end
 
class Bar < Foo
  def three
    one(x: ...)
  end
 
  def one(y)
    "1"
  end
end

Bar.new.three # => "One"