Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • error assigning returned class from static method

    I'm encountering an error in Mata interactive sessions when I call a static class method that returns a class and try to assign the returned instance to a variable. If the method is defined outside of a class everything works fine; if it is defined as a static method of a class, I get a "type mismatch: transmorphic = class not allowed" error.

    For example, if I define a basic class:
    Code:
    version 13.1
    mata:
    mata set matastrict on
    mata set matalnum on
    
    class myclass {
        real scalar x
        void dump()
        static class myclass scalar create()
    }
    
    void myclass::dump() {
        printf("myclass, x:%f\n",x)
    }
    
    class myclass scalar myclass::create() {
        class myclass scalar c
        c.x = 9
        return(c)
    }
    From an interactive session, if I try to capture the instance of myclass returned by myclass::create(), I get the error:
    Code:
    a = myclass::create()    /* <---- THROWS ERROR: "type mismatch: transmorphic = class not allowed" */
    But if I define the function exactly the same except outside the class, everything works fine:
    Code:
    // exact same as myclass::create() except not defined as a static class function
    class myclass scalar create2() {
        class myclass scalar c
        c.x = 9
        return(c)
    }
    
    a = create2()  /* (no error) */
    a.dump()       /* prints "myclass, x:9" */
    What's going on here? Am I doing something wrong?

    Thanks,
    Yosef

  • #2
    I don't think your create2() works exactly the same, because create2() does not use myclass::create().

    Here is a function that does use myclass::create().
    Code:
    class myclass scalar create3()
    {
        return(myclass::create())
    }
    You still you get no error in this case, as this output shows:
    Code:
    : a = create3()
    
    : a
      0x4233890
    (In other words, the conclusion is the same, but your function did not actually use myclass::create().)

    The problem seems to be in defining something to be transmorphic, then defining it to be a class instance. When I try to define create4()
    Code:
    transmorphic create4()
    {
        transmorphic c
        
        c = myclass::create()
        
        return(c)
    }
    the definition fails in the middle:
    Code:
    type mismatch:  transmorphic = class not allowed
    r(3000);
    In your interactive example, a was defined as transmorphic before it was given the results of myclass::create().



    Does it not work for you to create your class instances in the usual way? I.e., with myclass defined like
    Code:
    class myclass {
        real scalar x
        void new()
        void dump()
    }
    
    void myclass::new()
    {
        this.x = 9
    }
    
    void myclass::dump() {
        printf("myclass, x:%f\n",x)
    }
    and create instances like
    Code:
    : a = myclass()
    
    : a
      0x4233aa0

    Comment


    • #3
      Hmm, I think I misunderstood the purpose of your comparison between interactive use vs create2(). So, maybe ignore the first part of my post.

      Also, it does seem like a would first be transmorphic in a = myclass() before being set to a class instance. You would think then that this would lead to an error, but somehow it does not.

      Comment


      • #4
        What I mean by saying myclass::create() and create2() are exactly the same (except that one is defined as a static class method) is:
        • They are both functions that take no parameters and return a class myclass scalar instance
        • Their function bodies are identical
        So I would expect them to be perfect substitutes for each other. However, for some reason the returned value from myclass::create() cannot be assigned to a transmorphic variable, while the returned value from create2() can. Why would that be?

        I came across this in an interactive session, but your create4() example indicates the same problem exists with non-interactive code. (Interactive global variables are implicitly transmorphic, so this makes sense.) create4() throws an error, but the following works fine:
        Code:
        transmorphic create5()
        {
            transmorphic c
            c = create2()
            return(c)
        }
        Why does create5() work and create4() not work? This exactly parallels the original interactive case where a=create2() works and a=myclass::create() does not work, i.e. the value returned from create2() can be assigned to a transmorphic variable, but the value returned from myclass::create() cannot. Why? Both create2() and myclass::create() declare their return types as class myclass scalar and construct the value to be returned in exactly the same way!

        (And btw for background, the reason I'm doing this instead of the "usual" way of using a constructor is that I really want the create() method to take several arguments. Constructors can't take arguments in mata, so I was trying to use a static factory method to achieve the same ends. But these extra arguments aren't relevant to the error message at hand, so I omitted them from the simplified example.)

        Comment


        • #5
          Yeah, I had just come to the realization that I hadn't thought this through enough before making my first post, and was coming back to point that out. Sorry. Hopefully someone can come along and provide some real information.

          Comment


          • #6
            Hello
            The following code works fine. Note that in my create() function c is defined as a class and not as transmorphic. It seems that it has something to do with the the implicit definition of class as transmorphic in interactive mode and the explicit definition of c as transmorphic in non-interactive mode. The operator myclass::create() does not seem to apply in interactive mode. The static function works fine if you call it in the usual way, that is by creating an instance of the class first. Why it is so is kind of a mystery to me. Maybe someone at Stata Corp. (Bill Gould) could shed some light on this issue.
            Another mistery is why constructors cannot take arguments in Mata?
            Best
            Christophe


            Code:
             version 12.1
            clear mata
            mata:
            mata set matastrict on
            mata set matalnum on
              class myclass {
                real scalar x
                void dump()
                static class myclass scalar create()
             class myclass scalar create2()
              }
              void myclass::dump() {
                printf("myclass, x:%f\n",x)
            }
              class myclass scalar myclass::create() {
                class myclass scalar c
                c.x = 9
                return(c)
            }
              class myclass scalar myclass::create2() {
                class myclass scalar c
                c.x = 9
                return(c)
            }
               void main()
            {
              class myclass scalar a
                a = myclass::create()
              a.dump()
            }
              main()
              a = myclass()
            a = a.create()
            a.dump()
            // a = myclass()
            a = a.create2()
            a.dump()
              // class myclass scalar a
              end

            Comment


            • #7
              Have you contacted techincal support?

              My guess is this is either a bug or a documentation oversight.

              The issue isn't limited to static methods. It happens with other class methods, and it happens when returning other class types.

              Example:
              Code:
              class myclassB { }
              
              class myclassA {
                  class myclassB scalar createB()
              }
              
              class myclassB scalar myclassA::createB()
              {
                  return(myclassB())
              }
              Then in Mata:
              Code:
              : a = myclassA()
              
              : b = a.createB()
              type mismatch:  transmorphic = class not allowed
              r(3000);
              
              : b = myclassB()
              
              : b
                0x2194460



              Another mystery is why constructors cannot take arguments in Mata?
              They can, but the arguments are used for creating matrices of class instances.

              Code:
              : class myclass {}
              
              : myclass(4)
                             1           2           3           4
                  +-------------------------------------------------+
                1 |  0x4143350   0x413b780   0x4142fe0   0x4142430  |
                  +-------------------------------------------------+
              
              : myclass(2, 4)
                             1           2           3           4
                  +-------------------------------------------------+
                1 |  0x413b780   0x4143350   0x4127940   0x4141720  |
                2 |  0x4141460   0x4143090   0x2194720   0x2194670  |
                  +-------------------------------------------------+

              Comment


              • #8
                I guess my last point depends on whether new() is considered the constructor or myclass() is.

                Comment


                • #9
                  Dear James
                  By reading the documentation I conclude that new() is the constructor in the sense, that you can attribute initial values to the members of the class when you create an instance of that same class.
                  It cannot take arguments contrary to C++ or C#. Now I understand why you cannot use constructors with arguments.
                  As you mention it if you write a = myclass(2,2) you will create a 2x2 matrix of myclass objects. The new() will initialize each object of the matrix with the intended values as shown in the following example.

                  Code:
                   clear mata
                  mata:
                    class myclass {
                    real scalar m_x
                    
                    void new()
                  }
                    
                  void myclass::new()
                  {
                    m_x = 0
                  }
                   end  mata:
                  : a = myclass(2,2)
                    :
                  : a
                                 1           2
                      +-------------------------+
                    1 |  0x58b9180   0x58b92e0  |
                    2 |  0x2232000   0x2232160  |
                      +-------------------------+
                    :
                  : a[1,1].m_x
                    0
                  
                  : end
                  Another thing lacking is the overloading of functions, but it is another problem .
                  Apparently you cannot assign a class to a transmorphic object, contrary to structures. It looks to me, that it is something intended by Stata Corp. As you suggest I would be a good idea to contact the Technical Support about it though.
                  Best
                  Christophe


                  Comment


                  • #10
                    It's a bug. I just received the following response from technical support:

                    This is a known issue, and we will try to improve our parsing routine to allow assigning a returned class instance through a member function to an undeclared variable in future.

                    Comment

                    Working...
                    X