CORE - Literal Instantiation breaks Object Model

Discussion in 'Ruby' started by Ilias Lazaridis, Jun 9, 2011.

  1. class String
    alias_method :eek:rig_initialize, :initialize
    def initialize(val)
    orig_initialize "OBSERVED: " + val
    end
    def my_method_test
    print self.inspect, " test\n"
    end
    end

    oo_string = String.new("The OO String")
    li_string = "The Literal String"

    print "Class: ", oo_string.class, " - content: ", oo_string, "\n"
    print "Class: ", li_string.class, " - content: ", li_string, "\n"
    oo_string.my_method_test
    li_string.my_method_test

    #OUTPUT
    #=> Class: String - content: OBSERVED: The OO String
    #=> Class: String - content: The Literal String
    #=> "OBSERVED: The OO String" test
    #=> "The Literal String" test

    -

    The behaviour of the class String has been altered, whilst using the
    standard mechanisms of the Object Model.

    To my huge surprise, although the li_string has been instantiated as
    an object of class String, the new initialize method was not called.

    This is essentially a defect, as the consistency of the Object Model
    breaks.

    The statement "Everything is an Object" becomes invalid, because e.g.
    a string object instantiated from a literal behaves differently that a
    string object instantiated normally via new() (although they share the
    same class, and thus should behave the same).

    My understanding is, that this is a know-issue and a trade-off due to
    performance issues.

    The questions are:

    b) Is there any way to track (intercept) the instantiation of objects
    (especially those instantiated from literals)
    1) without a C-level extension
    2) with a C-level extension

    The interception can be post instantiation.

    Underlying Requirement:

    Ability to track instantiation of every object within the system.

    ..
     
    Ilias Lazaridis, Jun 9, 2011
    #1
    1. Advertisements

  2. If that really had worked the way you’d expect it, you had ended up in an endless loop running out of stack levels soon. The reason for this is that
    each string literal would have to go through your special initialization routine, including your "The OO String" and, very subtle, the "OBSERVED: "
    string as well. The former would make oo_string into having two times the "OBSERVED: " prefix, once for initializing the string literal, and second
    inside of your explicit String.new() call. But the fact that "OBSERVED: " would require string initialization as well, would lead to an endless loop.

    To fix this infinite loop, one would have to initialize a string object prior to hooking initialize() like this:

    observed_prefix = "OBSERVED: "

    class String
    alias_method :eek:rig_initialize, :initialize
    def initialize(val)
    orig_initialize observed_prefix + val
    end
    […]
    end

    As Matz already pointed out, there are quite some good reasons for letting the parser translate literal constants into objects independent from the
    actual program flow.
    Another question could be why »"OBSERVED: " + val« does not call initialize() on the resulting string right before that result is passed to
    orig_initialize. OTOH, this would lead to another endless loop.
    As you see from the above, it’s not just a trade-off for performance reasons.
    This is an interesting idea. I think you have to patch NEWOBJ() in the sources, at least.

    – Matthias
     
    Matthias Wächter, Jun 10, 2011
    #2
    1. Advertisements

  3. The logic in this argument is fundamentally flawed. "Everything is an
    Object" does not mean "every object of the same class behaves the
    same". Objects of the same class instantiated with different syntax
    regularly behave differently. Aside from literal vs. new, you have:

    #allocate vs. #new generally
    creation via other methods specific to a particular class (e.g., for
    Procs objects, proc {} vs. lambda {} vs. Proc.new vs ->() {} vs.
    method() vs. & to capture the block passed to a method)

    Others in the thread have explained adequately the practical reasons
    why you can't have <literal> be interpreted as
    <some-class>.new(<literal>) where both <literals> are identical [as
    that would produce infinite recursion], but aside from the
    practicality you are wrong on the basic theory: different
    initialization mechanism producing objects which are discernably
    different in the path of method calls involved in their instantiation
    and/or their behavior later in their lifecycle is not inconsistent
    with Ruby's object model, it is rather fundamental to it.
     
    Christopher Dicely, Jun 10, 2011
    #3
  4. #change to something like:

    orig_initialize val
    puts self.inspect
    [...] - (elaborations of recursion problems with current showcase)

    I've understood your elaborations.

    You should not focus on the showcase code, which could be easily
    rewritten to not fall into this "trap" at all (e.g. placing a simple
    "puts object.inspect" in the constructor).

    Please see the reply to Mr. Matsumoto for more details on the essence
    of this issue (and the general essence subjecting development).
    The performance problem is the central one. Everything else comes
    after, and should be solvable easily.
    Is "NEWOBJ()" the central function, used system-wide (even from the
    parser)?

    ..
     
    Ilias Lazaridis, Jun 10, 2011
    #4
  5. Related Issue:

    Literal Instantiation breaks Object Model
    http://redmine.ruby-lang.org/issues/4893

    Related Issue (Attempt to workaround)

    Provide Class#cb_object_instantiated_from_literal(object)
    http://redmine.ruby-lang.org/issues/4845

    ..
     
    Ilias Lazaridis, Jun 21, 2011
    #5
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.