dot operator behavior question

I’ve been using the ‘dot’ operator for some “classes” I’m using, but I’m hitting a road block when trying to build a dynamic dispatch table.

To give an example of what I need to accomplish consider the following function. Effectively, it returns the attribute specified if the ‘m’ variable is assigned as expected when the function return attr is called using the ‘.’ operator or a string stating that the attribute does not exist:

Function returnattr(attrname as String) as Object                                                                 
    if not m.DoesExist(attrname) then                                                                             
        return "Attribute " + attrname + " does not exist"                                                        
    else                                                                                                          
        return m.Lookup(attrname)                                                                                 
    end if                                                                                                        
End Function  

Now consider the following scenarios:

Sub test_dynamic_dispatch(t as object)                                                                            
    obj = CreateObject("roAssociativeArray")                                                                      
    obj["getattr"] = returnattr                                                                                   
    obj["a"] = "a"                                                                                                
                                                                                                                  
    'Using dot operator to perform call                                                                           
    t.assertEqual("a", obj.getattr("a"))                                                                          
                                                                                                                  
    'Using dot operater in an eval()                                                                              
    result = "NO VALUE ASSIGNED"                                                                                  
    attrname = "a"                                                                                                
    code_snippet = "result = obj.getattr(attrname)"                                                               
    eval(code_snippet)                                                                                            
    t.assertEqual("a", result)                                                                                    
                                                                                                                  
    'Using dot operator to do method dispatch                                                                     
    method = obj.getattr                                                                                          
    t.assertEqual("a", method("a"))                                                                               
End Sub                                        

The first (explicitly using the dot operator) and second (using eval) approaches work as expected. In the case of the third (looking up the method then calling it) the ‘m’ variable is assigned to an empty roAssociativeArray. Is to be expected that the only time ‘m’ will be set is when the method is explicitly called with the dot operator even if the method is accessed with the dot operator initially?

-Mark

Mark,

Yes, the current behavior is that the only time ‘m’ will be set is when the method is explicitly called with the dot operator. I suggest not relying on this behavior in any of your code, as we will continue to expand BrightScript’s capabilities in the future. Including dynamic dispatch, closures, etc… could reasonably make the cut at some point.

Your eval() technique above is probably a reasonable work-around for the time being in scenarios like unit-test frameworks.

–Kevin

“RokuKevin” wrote:
Mark,

Yes, the current behavior is that the only time ‘m’ will be set is when the method is explicitly called with the dot operator. I suggest not relying on this behavior in any of your code, as we will continue to expand BrightScript’s capabilities in the future. Including dynamic dispatch, closures, etc… could reasonably make the cut at some point.

Your eval() technique above is probably a reasonable work-around for the time being in scenarios like unit-test frameworks.

–Kevin

Kevin,
Thanks for the response. I’ve started using the eval() technique for the time being.

When you say not to rely on the behavior do you mean the dot operator/‘m’ variable in general (as I’m using it quite a bit currently)? Or do you mean the distinction of when the ‘m’ variable is set or not?

Also, I’m VERY happy to hear that there’s a possibility to include closures. I was disappointed when I realized the first time I used anoymous function to find that I couldn’t close over variables in the containing scope.

-Mark

Mark,

You can count on the ‘m’ variable to be set when the method is explicitly called with the dot operator.

I meant don’t count on the distinction of when the ‘m’ variable is set or not…

–Kevin

“RokuKevin” wrote:
Mark,

You can count on the ‘m’ variable to be set when the method is explicitly called with the dot operator.

I meant don’t count on the distinction of when the ‘m’ variable is set or not…

–Kevin

Kevin,
Thanks for the clarification.

-Mark

Kevin,
Just an FYI, the approach of using eval() discussed here was how I attempted to implement a dispatch table, but unfortunately it does not seem to be working in my case due to what appears to be a bug causing the DVP to crash when used recursively (something I need to do for my use case). The following code reproduces the issue:

Sub Main()                                                                                                        
    result = SomeFunction(100)                                                                                    
End Sub                                                                                                           
                                                                                                                  
Function SomeFunction(x as integer) as integer                                                                    
    if x = 0 then                                                                                                 
        return 0                                                                                                  
    else                                                                                                          
        x = x -1                                                                                                  
        result = -1                                                                                               
        eval("result = SomeFunction(x)")                                                                          
        return result                                                                                             
    end if                                                                                                        
End Function                                                                                                      

In the case of the dispatch table I implemented this would cause crashes non-deterministically so I’m not entirely sure where the issue is. I can only guess it has to do with using eval() from within the context of another eval() call. For the time being I’ve switched to using an ugly if/else if/else if/…/else statement.

-Mark

Mark,

Thanks for the crash report. I’ve opened a bug for it.

Kevin

eval() performs a compile, so it is slower than you might expect.

“RokuAnthony” wrote:
eval() performs a compile, so it is slower than you might expect.

Thanks Anthony. Unfortunately, there are several pieces of dynamic functionality I need that currently can only be implemented via calling eval(). On the up side, ‘slow’ is not as much of an issue for the testing framework I’m working on, especially considering there is no UI (at this point).

-Mark

FYI, I think (and my testing shows) there IS a way to handle this without eval. Use the alternate AssociativeArray syntax (square brackets).


function testclass() as object
        this = {
                foo: "bar"
                func: function() as boolean
                        print "in method, foo is "; m.foo
                        for each attr in m
                                print "  found "+attr+" in m"
                        end for
                end function
        }
        return this
end function

sub main()
        c=testclass()
        c.func()
        c["func"]()
end sub

You get this output, which implies c.func() and c"func" result in the same behavior.


in method, foo is bar
  found func in m
  found foo in m
in method, foo is bar
  found func in m
  found foo in m

That seems to imply it’s not really the dot operator, but the associative array existence that sets m. I don’t know whether this was changed since this was mentioned here, just noting how I see it working currently.

Thanks kbenson!

I love to learn new techniques from our dev community.

–Kevin

Note that the technique of doing “dynamic dispatch” with the associative array syntax:


c["func"]() 

will only work if the underlying object is an associative array. This is not true for objects created with the CreateObject() function. This technique will not work with those objects, and you are back to using Run() or eval()

–Kevin

I suspected as much, but hadn’t bothered to test. Luckily, that can be solved through a simple wrapper class that just dispatches to the component methods. Obviously there will by a little overhead (probably negligible unless there’s LOTS of calls), but also the ability to put in sane defaults to the common use cases. In all, a fair trade-off in my opinion.

“RokuKevin” wrote:
…we will continue to expand BrightScript’s capabilities in the future. Including dynamic dispatch, closures, etc… could reasonably make the cut at some point.

I know it’s a long shot, and there’s other stuff to work on that’s more important, but closures would be really useful right about now. Are there any scheduled plans for them in the future, or is it still a “it’s possible” thing? Anonymous functions are great, but anonymous functions with closures are awesome.

There are no immediate plans to provide closures in the language. I agree they would be useful.

–Kevin