Immediate If

Monkey Forums/Monkey Programming/Immediate If

Nobuyuki(Posted 2012) [#1]
Quick Q. Does Monkey have Immediate If? Something like a pseudofunction or ternary operator, like such:

Basic-style:
Iif(expression, truepart, falsepart)


C-style:
 expression ? truepart : falsepart 



jpoag(Posted 2012) [#2]
From the language reference...

"In addtion, a simple one line version of If is also supported:"

If Expression [ Then ] Statement [ Else statement ]


I'm not sure that returns a value.


jpoag(Posted 2012) [#3]
Ok, I tested it out:

Function Main:Int()
	Local retVal:Int = 0;
	
	Local cond:Bool = false;
	
	'retVal = (If cond Then 1 Else 9); ' doesn't work
	If cond Then retVal = 1 Else retVal = 9;
	
	Print(retVal);

	Return 0;
End



Neuro(Posted 2012) [#4]
Kinda sloppy but you can also try something like this :




Samah(Posted 2012) [#5]
The problem with that is that both results are evaluated regardless of the condition boolean. A c-style ternary operator won't do that. It will only evaluate the result it needs to.


ziggy(Posted 2012) [#6]
@Samah: In Monkey it's the same, only the required operator is evaluated, and it is clearer as it is shown in the form of program flow.
If True Then  Print("Hello") Else Print("Bye")

Bye is never printed. So second expression is not evaluated.
In this example:
If False Then  Print("Hello") Else Print("Bye")

Hello is never printed.
In other languages the IIF function does in fact evaluate all its parameters, wich is a lot unuseful.


Samah(Posted 2012) [#7]
@Ziggy: I'm not talking about the one line If, I'm talking about the call to IIf. I can do a simple If like that in just about any language, but it's not the same as a proper ternary operator that you can inline in an equation.


ziggy(Posted 2012) [#8]
Of course! I was misreading your post


Samah(Posted 2012) [#9]
Of course! I was misreading your post

<3

I do however agree that a proper ternary operator would be fantastic. A coalesce operator would be great too.


Nobuyuki(Posted 2012) [#10]
I do however agree that a proper ternary operator would be fantastic. A coalesce operator would be great too.


+1 on coalesce. Ternary operation may break the flow of a Basic dialect for readability unless proper care is taken. ":" is already used for us here to define type-related stuff, so I'm wondering if "|" might be an acceptable alternative. I'm completely fine with "??" as a null-coalescing operator, however.


Samah(Posted 2012) [#11]
I believe pipe is used for a bitwise OR. It'd probably have to be an actual keyword like Coa.


Nobuyuki(Posted 2012) [#12]
oh, well I meant for the second half of the iif ternary operator. I guess an overload for "," could work too, as was done in CPL. As for null coalescense, I'd still be for "??" or a keyword token like OrIfNull ...


jpoag(Posted 2012) [#13]
I'm reading through the parser and Monkey uses top-down LL parsing with no backtracking and no look-ahead. I suggest you open parser.monkey to follow along.

Keep in mind some of the differences between assignment statements and If statements. The right-hand-side of an assignment statement has to be an expression (not a statement). That's why IF blocks can't be used to return a value (not that they do anyways).

So you couldn't do this:

Identifier = Iif expression, truepart, falsepart

because as soon as the parser hits the '=' token, it becomes an assignment operation that expects an expression on the right.

How To
I can think of two ways to get a ternary operator. The more complicated way involves extending the language all the way down to the LANG specific translators. That's a lot of work.

The easier way is to translate the ternary operation into the existing IFStmt by generating an AssignStmt for both the Then/Else blocks.

To get a ternary assignment operation, you would need to:

* define the operator, something like =? (?= is taken)
* modify ParseStmt() and add a case for "=?" in the Default section
* create a ParseTernStmt() that generates a IfStmt, so that:

Identifier =? (bool expression) ? trueexpression : falseexpression

turns into
IF bool expression THEN Identifier = trueexpression ELSE Identifier = falseexpression


The IfStmt takes blocks for THEN and ELSE. You can manually add Statements to the blocks, specifically AssignStmt:

thenStmt = new AssignStmt("=", IdentifierExpr, trueExpression);
thenBlock.AddStmt(thenStmt);

... elseStmt & elseBlock

_block.AddStmt New IfStmt( boolExpr,thenBlock,elseBlock )

Recursive ?:
http://en.wikipedia.org/wiki/%3F:#.3F:_in_style_guidelines

Once you're parsing the Ternary statement, then you can recursively parse the trueExpression and falseExpression for more ternary statements. ParseIfStmt() is a good example as 'ElseIf' is really a recursive IF block being pushed on the stack.

That lets you do something like
vehicle =? arg = 'B' ? bus :
           arg = 'A' ? airplane :
           arg = 'T' ? train :
           arg = 'C' ? car :
           arg = 'H' ? horse :
                       feet;

Coalesce
The coalesce could be faked with the ternary operator looking for a special case token '??'

Identifier =? theObject ?? theDefault

translates into:

IF theObject<>NULL THEN Identifier = theObject ELSE Identifier = theDefault

The special case is that the ternary operator doesn't look for the ':' token or subsequent falseExpression.

Obviously, this only works for nullable types.