Undocumented: Expressions properly shortcircuiting

Monkey Forums/Monkey Programming/Undocumented: Expressions properly shortcircuiting

ziggy(Posted 2012) [#1]
I was answering a sort of "bug report" on the Jungle Ide forum that was related to a feature of Monkey that I think is not properly documented, wich is how monkey shortcircuits some expressions when it can.
It can produce some unexpected side effects if you're not used to it and you do have a bit unstandard way of coding.

(this comes originaly from this post)

As I think this can be valuable to some newbies, I'm pasting the details here:

----

Accessing instanceless members of a class (functions or consts) should be done by using the Class name, and not any instance. It is considered a bad practicle to access shared (static, non instance) members of a class due an instance, as the access to the instance can be bypassed at the compilation stage. And that could leave part of an expression unexecuted and uneavluated.

As instance, accessing a class constant using a function that returns a class instance could be translated at compiletime by the constant value directly, avoiding the function call.

This is a code example showing this particular design (wonderful) feature:
Import mojo
Function Main()
	Local myclass:= New MyClass
	Print myclass.GetInstance().MYCONST
End
Class MyClass
	Const MYCONST:Int = 34
	Function GetInstance:MyClass()
		Print "Function Called!"
		Return New MyClass
	End
	Method DoSomething:Int()
		Print "Method Called!"
		Return 12345
	End
End

You might expect the output to be:
Function Called!
34

but oh surprise, "Function Called!" is never shown (tested on HTML5 and stdcpp removing import mojo)

Now, in this other sample:
'Import mojo
Function Main()
	Local myclass:= New MyClass
	myclass.GetInstance().GetInstance().GetInstance().GetInstance()
End
Class MyClass
	Const MYCONST:Int = 34
	Function GetInstance:MyClass()
		Print "Function Called!"
		Return New MyClass
	End
	Method DoSomething:Int()
		Print "Method Called!"
		Return 12345
	End
End

One could expect the text "Function Called!" to be printed four times, but it appears only once, as the whole expression:
myclass.GetInstance().GetInstance().GetInstance().GetInstance() 

Is resolved at compile time. That makes code faster, but it can be confusing if you use instance values to access shared/static members of a class.

This:
myclass.GetInstance().GetInstance().GetInstance().GetInstance()

Should be:
MyClass.GetInstance()

To make code readable and easier to maintain. You read Myclass.GetInstance() and you KNOW it is a function call, not a method, nor anything else. Also, you know the expression has nothing to evaluate other than resolve which function to call.

Knowing this, I designed Jungle Ide intellisense to show instance members when intellisense is used on an class instance, and static/shared members when it is used on a Class name, in order to help people stay in what I considere a sensible coding style.


muddy_shoes(Posted 2012) [#2]
Regardless of the sensibility of the coding style it's not a valid "short-circuit" optimisation to completely skip intended side-effects of function calls. That's a bug.


ziggy(Posted 2012) [#3]
I would not considere a bug. It may be better if you could not use an instance to reference a static member of a class (and getting a compiler error in this case, ala C#, or a warning ala VB). But I do really not see it as a bug. If you ask me, this scenario should produce a warning, but Mark seems to be not very inclined to add compiler warnings to Trans. Now you make me wonder if we should place a bug report, just to get an official response of this scenario?


muddy_shoes(Posted 2012) [#4]
I honestly can't follow how you don't see this as a bug. The function is explicitly called in the code and that call is not honoured in the final result. It's not a short-circuit in the manner of a boolean operator, it's simply not doing something that the code requests to be done.

The function could be doing anything before returning the object. That it works if you just call GetInstance() and ignore the result and doesn't if you happen to use the instance to reference a class variable, constant or function makes no sense.

As for getting the official word on it, sure, go ahead. I won't be surprised if Mark says it's "working as intended" but that wouldn't change my mind about it being a defective optimisation.


Gerry Quinn(Posted 2012) [#5]
I agree that this should be considered a bug. I thought that perhaps the Print function was considered a special case that didn't count as a side effect, but when I modified it so that GetInstance() changes a global variable, it still didn't get called.


maltic(Posted 2012) [#6]
As far as I see it this is a bug. Monkey isn't a functional programming language by a long shot. Everything revolves around global stateful changes. If state changing code gets optimized away thats an optimization bug. However the water seems muddied by the fact we are using a print statement which only requires a final value to output. So what happens when we delay that print statement?

Import mojo
Function Main()
	Local myclass:= New MyClass
	Local x:= myclass.GetInstance().MYCONST
	Print x
End
Class MyClass
	Const MYCONST:Int = 34
	Function GetInstance:MyClass()
		Print "Function Called!"
		Return New MyClass
	End
	Method DoSomething:Int()
		Print "Method Called!"
		Return 12345
	End
End


In this case "Function Called!" still doesn't get called. Imagine if you had a counter in get instance that kept track of the number of instances created for memory usage metadata. This could lead to some serious bugs.


Difference(Posted 2012) [#7]
I vote bug.
Anything else is too cryptic


ziggy(Posted 2012) [#8]
I would love an official answer on this.


marksibly(Posted 2012) [#9]
Hi,

Definitely a bug - will fix!