Announcement

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

  • Overloaded method definitions

    I was wondering if anyone knew how to define overloaded methods in mata? I have an arbitrary example here:


    Code:
    mata
    mata clear
    
    // Method if scalar is passed to the function
    real matrix sqelem(real scalar x) {
         real matrix matv
         matv = (J(x, 1, (1::x)))
         return((matv[1::rows(matv), 1]:*matv[1::rows(matv), 1]))
    }
    
    // Method if matrix is passed to the function
    real matrix sqelem(real matrix x) {
         return((x[1::rows(x), 1]:*x[1::rows(x), 1]))
    }
    Running this leads to several error messages:

    Code:
    : mata clear
    
    : real matrix sqelem(real scalar x) {
    > real matrix matv
    > matv = (J(x, 1, (1::x)))
    > return((matv[1::rows(matv), 1]:*matv[1::rows(matv), 1]))
    > }
    
    : real matrix sqelem(real matrix x) {
    sqelem() already exists
    r(3000);
    
    : return((x[1::rows(x), 1]:*x[1::rows(x), 1]))
    'return' found where almost anything else expected
    r(3000);
    
    : }
    expression invalid
    r(3000);
    Each function has a different signature for the arguments and these types of method definitions are fairly typical in other languages, so I was wondering if anyone else had come across anything related to this. Also, if this is covered somewhere in the documentation that I've overlooked I'd appreciate any references to existing documentation for this.

  • #2
    I don't know if there is a better way of doing this, but one possibility is to just have an if statement that detects whether or not a scalar has been inputted - that is, using the fact that a scalar is a 1X1 matrix. Something like:
    Code:
    mata
    mata clear
    real matrix sqelem(real matrix x) {
        real matrix matv
        if (length(x)==1) {
            matv = (J(x, 1, (1::x)))
            return((matv[1::rows(matv), 1]:*matv[1::rows(matv), 1]))    
        }
        else {
            return((x[1::rows(x), 1]:*x[1::rows(x), 1]))
        }
    }
    end
    In more complex cases, one can take in the argument x as transmorphic, and then detect whether or not it is something using the eltype() function.

    Hope that helps!

    Matt


    Comment


    • #3
      Hi Matthew J. Baker,

      Thanks for the suggestion. Ideally, I wanted to create a method/function with several signatures to cover a variety of different scenarios (e.g., allow the end user to pass a numlist, a matrix, different numbers of scalars, etc...). So, it might look something more like:

      Code:
      mata
      mata clear
      
      // Method if scalar is passed to the function
      real matrix sqelem(real scalar x) {
          real matrix sequence
          sequence = (J(x, 1, (1::x)))
          return((sequence[1::rows(sequence), 1]:*sequence[1::rows(sequence), 1]))
      }
      
      // Method if matrix is passed to the function
      real matrix sqelem(real matrix x) {
          return((x[1::rows(x), 1]:*x[1::rows(x), 1]))
      }
      
      real matrix sqelem(real scalar startSequence, real scalar endSequence) {
          real matrix sequence
          sequence = (startSequence::endSequence)
          return((sequence[1::rows(sequence), 1]:*x[1::rows(sequence), 1]))
      }
      
      real matrix sqelem(real scalar endSequence) {
          real matrix sequence
          sequence = (1::endSequence)
          return((sequence[1::rows(sequence), 1]:*x[1::rows(sequence), 1]))
      }
      
      real matrix sqelem(string scalar stmatName) {
          real matrix sequence
          sequence = st_matrix(stmatName)
          return((sequence[1::rows(sequence), 1]:*x[1::rows(sequence), 1]))
      }
      
      real matrix sqelem(string scalar stlocName) {
          real matrix sequence
          sequence = (st_local(stlocName))
          return((sequence[1::rows(sequence), 1]:*x[1::rows(sequence), 1]))
      }
      
      end
      The code here is just a trivial example, but is intended to illustrate conceptually what it is that I am trying to do. I've not tried declaring the functions as methods of a class, but can try that a bit later to see if it works.

      Comment


      • #4
        Overloading of functions (like the way you wish) is not possible with Mata as far as I know. And it is not possible with a class either. As suggested by Matthew passing arguments as transmorphic and the use of optional arguments would allow to have a generic function. You could also consider a wrapper for your functions which you have to call differently.

        Aside: I cannot see how Mata will distinguish case 5 and 6. They have the same argument and return the same thing.

        Comment


        • #5
          Mata does not support function overloading. You can simulate function overloading with arity (same function name but different number of arguments) by using optional arguments. You can simulate function overloading with different data types by declaring the datatype as transmorphic, then inspect the argument using eltype() and orgtype() at runtime.

          Comment


          • #6
            Christophe Kolodziejczyk
            Originally posted by Christophe Kolodziejczyk View Post
            Overloading of functions (like the way you wish) is not possible with Mata as far as I know. And it is not possible with a class either. As suggested by Matthew passing arguments as transmorphic and the use of optional arguments would allow to have a generic function. You could also consider a wrapper for your functions which you have to call differently.

            Aside: I cannot see how Mata will distinguish case 5 and 6. They have the same argument and return the same thing.

            I realized a bit after I had sent the post that there were two definitions with identical signatures. The first and fourth are also the same (albeit with a different argument name).

            Hua Peng (StataCorp) is multiple dispatch/method overloading something that would potentially be on the horizon that could be discussed publicly?

            Comment


            • #7
              It is a useful language construct but not on any short development list I am aware of.

              Comment


              • #8
                Hi Hua Peng (StataCorp) thanks for the info.

                Comment


                • #9
                  Originally posted by Hua Peng (StataCorp) View Post
                  Mata does not support function overloading. You can simulate function overloading with arity (same function name but different number of arguments) by using optional arguments. You can simulate function overloading with different data types by declaring the datatype as transmorphic, then inspect the argument using eltype() and orgtype() at runtime.
                  My understanding is that transmorphic only refers to eltype, and if you don't declare an orgtype matrix is assumed. How, then can we overload the same method to accept a scalar or a colvector for the same argument, for example? Or does Mata automatically transform a matrix into a scalar if a scalar is passed, and to a colvector if a colvector is passed?

                  Thanks.

                  Alfonso Sanchez-Penalver

                  Comment


                  • #10
                    Originally posted by Alfonso Sánchez-Peñalver View Post
                    . . . does Mata automatically transform a matrix into a scalar if a scalar is passed, and to a colvector if a colvector is passed?
                    Yeah, it seems.
                    Code:
                    version 18.0
                    
                    clear *
                    
                    mata:
                    mata set matastrict on
                    
                    void function whatisit(transmorphic argument) {
                        printf("Orgtype is %s\n", orgtype(argument))
                        printf("Eltype is %s\n", eltype(argument))
                    }
                    
                    argument = 1
                    whatisit(argument)
                    
                    argument = 1 \ 2 \ 3
                    whatisit(argument)
                    whatisit(argument')
                    
                    end
                    
                    exit
                    :ÿ
                    :ÿvoidÿfunctionÿwhatisit(transmorphicÿargument)ÿ{
                    >ÿÿÿÿÿÿÿÿÿprintf("Orgtypeÿisÿ%s\n",ÿorgtype(argument))
                    >ÿÿÿÿÿÿÿÿÿprintf("Eltypeÿisÿ%s\n",ÿeltype(argument))
                    >ÿ}

                    :ÿ
                    :ÿargumentÿ=ÿ1

                    :ÿwhatisit(argument)
                    Orgtypeÿisÿscalar
                    Eltypeÿisÿreal

                    :ÿ
                    :ÿargumentÿ=ÿ1ÿ\ÿ2ÿ\ÿ3

                    :ÿwhatisit(argument)
                    Orgtypeÿisÿcolvector
                    Eltypeÿisÿreal

                    :ÿwhatisit(argument')
                    Orgtypeÿisÿrowvector
                    Eltypeÿisÿreal

                    Comment


                    • #11
                      Originally posted by Joseph Coveney View Post
                      Yeah, it seems.
                      Code:
                      version 18.0
                      
                      clear *
                      
                      mata:
                      mata set matastrict on
                      
                      void function whatisit(transmorphic argument) {
                      printf("Orgtype is %s\n", orgtype(argument))
                      printf("Eltype is %s\n", eltype(argument))
                      }
                      
                      argument = 1
                      whatisit(argument)
                      
                      argument = 1 \ 2 \ 3
                      whatisit(argument)
                      whatisit(argument')
                      
                      end
                      
                      exit
                      :ÿ
                      :ÿvoidÿfunctionÿwhatisit(transmorphicÿargument)ÿ{
                      >ÿÿÿÿÿÿÿÿÿprintf("Orgtypeÿisÿ%s\n",ÿorgtype(argument))
                      >ÿÿÿÿÿÿÿÿÿprintf("Eltypeÿisÿ%s\n",ÿeltype(argument))
                      >ÿ}

                      :ÿ
                      :ÿargumentÿ=ÿ1

                      :ÿwhatisit(argument)
                      Orgtypeÿisÿscalar
                      Eltypeÿisÿreal

                      :ÿ
                      :ÿargumentÿ=ÿ1ÿ\ÿ2ÿ\ÿ3

                      :ÿwhatisit(argument)
                      Orgtypeÿisÿcolvector
                      Eltypeÿisÿreal

                      :ÿwhatisit(argument')
                      Orgtypeÿisÿrowvector
                      Eltypeÿisÿreal
                      Interesting.

                      Thanks!
                      Alfonso Sanchez-Penalver

                      Comment

                      Working...
                      X