cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Browse apps to extend the software in the new JMP Marketplace
Choose Language Hide Translation Bar
ErraticAttack
Level VI

JMP Expr() weirdness, can anyone explain?

This is mostly just a frustration post over the following inconsistency with inserting 'IF' expressions into an 'IF' expression.  It is my understanding that programming languages should be nothing if not consistent (and I'm aware of many other JMP inconsistencies -- it's just that this one makes no sense to me at all).

 

/*
What I want is to generate an expression such as the one below programmatically.  There of course is a straight-forward work around, I just don't like dealing with special cases especially when they make no sense.
Expr( If( a, If( b, c) ) )
*/ expr = Expr( If( a ) ); Insert Into( expr, Expr( If( b, c ) ) ); Show( Name Expr( expr ) ); /* If( a, b, c) */ expr = Expr( not an If( a ) ); Insert Into( expr, Expr( If( b, c ) ) ); Show( Name Expr( expr ) ); /* not an If( a, If( b, c) ) */

If anyone has any insights, or especially if you know of a way to reliably insert any expression into any other expression (not by using 'Eval Insert' or strings), that would be great!  Thanks!

Jordan
10 REPLIES 10
Craige_Hales
Super User

Re: JMP Expr() weirdness, can anyone explain?

This is closely related to the same as How to insert a list into another list . With your question, I now suspect there is a special exception for builtin function names that shows this behavior.

expr = Expr( not an If( a ) );

is the same as

expr = Expr( NotAnIf( a ) );

making NotAnIf (or not an if) be a function name JMP doesn't recognize as builtin. In the { list } example, {...} is the same as List(...) and is a builtin function name.


expr = Expr( sum( a ) );
Insert Into( expr, Expr( sum( b, c ) ) );
Show( Name Expr( expr ) );
/*:

Name Expr(expr) = Sum(a, b, c);
//:*/

expr = Expr( sum1( a ) );
Insert Into( expr, Expr( sum1( b, c ) ) );
Show( Name Expr( expr ) );
/*:

Name Expr(expr) = sum1(a, sum1(b, c));

 

I don't like it either.

@XanGregg 

Craige
ben_ph
Level III

Re: JMP Expr() weirdness, can anyone explain?

As a workaround without using strings you could do something like

 

expr = Expr( If( a ) );
Insert Into( expr, Expr( REPLACE_ME ) );
Substitute Into( expr, Expr( REPLACE_ME ), Expr( If( b, c ) ) );
Name Expr( expr );
// If( a, If( b, c ) )

 

Re: JMP Expr() weirdness, can anyone explain?

Hello,

 

Although I am unsure, I wonder if this behavior is because the snippet "IF ( a )" is not a well-formed JSL expression. That is, the function IF takes 2 arguments, not one. JMP has no way of knowing whether "notAnIf( a )" is a well-formed expression, so perhaps shrugs its shoulders and proceeds ( ? ) Total guess on my part.

 

The fact the following works as you intend seems to add some support to this idea, but this does not explain why@ben_ph 's idea works, as it successfully inserts REPLACE_ME where I've got a missing.

 

expr = Expr( If( a, . ) );
Substitute Into( expr, expr(.), expr( If( b, c ) ) );
Show( Name Expr( expr ) );

brady_brady_0-1626712390488.png

 

Cheers,

Brady

hogi
Level XII

Re: JMP Expr() weirdness, can anyone explain?

Further examples which easily disprove some of the hypotheses - and add some further insights:

 

expr = Expr( If( a, x ) );
Insert Into( expr, Expr(If( b,c )) );
Show( Name Expr( expr ) ); // if(a,x,b,c)

expr = Expr( If( a ) );
Insert Into( expr, Expr(If( b )) );
Show( Name Expr( expr ) ); // if(a,b)

expr = Expr( List( a ) );
Insert Into( expr, Expr(List( b )) );
Show( Name Expr( expr ) ); // List(a,b)

expr = Expr( Graph Builder( a ) );
Insert Into( expr, Expr(Graph Builder ( b )) );
Show( Name Expr( expr ) ); // Graph Builder(a,b)


expr = Expr( Head( a ) );
Insert Into( expr, Expr(Head( b )) );
Show( Name Expr( expr ) ); // Head(a,b) (!)

expr = Expr( Not( a ) );
Insert Into( expr, Expr(Not( b )) );
Show( Name Expr( expr ) ); // Not(a) (!!)
hogi
Level XII

Re: JMP Expr() weirdness, can anyone explain?

there is a workaround to accomplish the requested task - but the actual question: WHY?!? is not yet answered.
added to Tiny Traps in Jmp and JSL 

Craige_Hales
Super User

Re: JMP Expr() weirdness, can anyone explain?

Consistency from release-to-release is also important. Every language has some design flaws that are made early on. Someone (typically a developer and a tech support specialist or sales engineer) decides if 

LongTermGainFromFixing - CostsOfFixing > CostOfDoingNothing

CostsOfFixing is really complicated, developer time + testing time + documentation time + short term tech support time + customer irritation that JSL that worked before now fails + missed opportunities + the risk of making it worse.

CostOfDoingNothing is also complicated: how much tech support is required, forever into the future, number of users that will ever hit the issue, and how hard is it to understand and work around. More guesswork.

This forum, and the wish-list forum, and your JMP representative, and user group meetings all help steer that decision. Thanks @hogi  for helping steer!

@julian 

 

Craige
ih
Super User (Alumni) ih
Super User (Alumni)

Re: JMP Expr() weirdness, can anyone explain?

This is a interesting problem.  You could create your own function to do the insert into which works as expected [I think], for all cases except the last one from @hogi.  For that case though the function returns the same result as when that expression is called using Name Expr directly, so one could argue that this is the 'correct' result.

 

Update: the function below has a bug, only the first instance should be replaced..

 

Names default to here(1);

// Make a new function to show each step of Insert Into:
New Insert Into = function( {exp, newarg, debug = 0 }, local({hd, ar, ne},
	//Get the outermost function
	hd = Head(exp);
	if(debug, show(name expr( hd ) ) );
	
	//Replace that function with a list so insert into works as expected
	ar = substitute(name expr( exp ), name expr( hd ), {});
	if(debug, show(name expr( ar ) ) );
	
	//Now add the new argument
	Insert Into( ar, name expr( newarg ) );
	if(debug, show(name expr( ar ) ) );
	
	//and put the orignial function back
	ne = substitute(name expr( ar ), {}, name expr( hd ));
	if(debug, show(name expr( ne ) ) );
	
	Name expr( ne );
) );

// Test with the examples in the original post, note that they match:
Show( New Insert Into( Expr( If( a )            ), Expr( If( b, c )         ) ) ); // If(a, If(b, c))
Show( New Insert Into( Expr( Not an If( a )     ), Expr( If( b, c )         ) ) ); // Not an If(a, If(b, c))

// Additional examples from hogi post
Show( New Insert Into( Expr( If( a, x )         ), Expr( If( b, c )         ) ) ); // If(a, x, If(b, c))
Show( New Insert Into( Expr( If( a )            ), Expr( If( b )            ) ) ); // If(a, If
Show( New Insert Into( Expr( List( a )          ), Expr( List( b )          ) ) ); // List(a, b)
Show( New Insert Into( Expr( Graph Builder( a ) ), Expr( Graph Builder( b ) ) ) ); // Graph Builder(a, Graph Builder(b))
Show( New Insert Into( Expr( Head( a )          ), Expr( Head( b )          ) ) ); // Head(a, Head(b))
Show( New Insert Into( Expr( Not( a )           ), Expr( Not( b )           ) ) ); // !a

// The last one doesn't seem right but that it is actually being 'fixed' after as it is created:
Show( New Insert Into( Expr( Not( a )           ), Expr( Not( b )           ), debug = 1 ) ); // !a

// This is the same result as if you just call that directly, so I think this is the 'correct' result
Name Expr( Not(a, !b) ); // !a

// **BUG**
// This function replaces all the Head expressions, note this line:
// Name Expr(ar) = {a, {b, c}}
Show( New Insert Into( Expr( If( a, If( b, c ) ) ), Expr( If( d, e ) ), 1 ) ); // If(a, If(b, c), If(d, e))
// So this is wrong:
Show( New Insert Into( Expr( If( a, { b, c } ) ), Expr( If( d, e ) ), 1 ) ); // If(a, If(b, c), If(d, e))

Here is the same function without the debug stuff:

New Insert Into = function( {exp, newarg }, local({hd, ar, ne},
	hd = Head(exp);
	ar = substitute(name expr( exp ), name expr( hd ), {});
	Insert Into( ar, name expr( newarg ) );
	ne = substitute(name expr( ar ), {}, name expr( hd ));
	name expr( ne );
) );

 

hogi
Level XII

Re: JMP Expr() weirdness, can anyone explain?

This fixes the issue with 2x the same type of expression. Yes.

 

But I fear, it's not as universal a replacement for Insert Into as one might wish.

Show( New Insert Into( Expr( If( a )), Expr( List( b,c )) ) ); // If(a,b,c)

 

hogi
Level XII

Re: JMP Expr() weirdness, can anyone explain?

Nevertheless, my main intention here:
Understand WHY Insert Into flattens the structure - such that whenever I use the function in the future, I can be sure and confident that it will do the right thing.

 

For 2 lists, it looks like a helpful function: it automatically generates a flattened list.
Hm, as helpful as the auto-range adjustment in Graph Builder - i.e.: for 93% of the cases it will help, for the other cases ...
Heatmap - option: fixed Tick spacing (aggregation area) 

 

Is there more behind it than trying to be "helpful"?

New to me was that it is not just trying to be helpful with list ...

 

Concerning "solutions" for Insert Into with the special case of 2x the same type of expression:

  • Maybe there is a hidden option: flatten(0|1)?
  • expr = Expr( If( a ) );
    Insert Into( expr, Expr( REPLACE_ME ) );
    Substitute Into( expr, Expr( REPLACE_ME ), Expr( If( b, c ) ) );
    Name Expr( expr );
    is wonderful

 

NB: 


@ih wrote:

... only the first instance should be replaced..


I had a similar problem some days ago and came up with the idea : Expression Indexing: read and write access.

It could take expression handling in Jmp/JSL to a new level. If you find it useful, you might vote for it ...

[I added a comment with a link to this post.]