Virtual rabbit hole ahead. Mark's answer may be all you need.
Is a fascinating question. It sure looks like you should get the same answer, but...
Unlike C and many other languages, the arguments to functions are not evaluated before the function is called. Instead the function determines how to handle its parameters. And, JSL has a variable type called expression, closely related to the value AsName returns. When you call a function with an expression argument, the function can choose what to do with the parameter. It might evaluate the expression; a+2 requires looking up the variable a and adding 2. Or it might manipulate the expression; head(a+2) returns add. In your example, the expression is abc, and looking it up means getting its value from a row in the data table.
(This is hard, I spent an hour trying to make the comments correct and clear. I think they are correct.)
dt = New Table( "test", New Column( "abc" ), New Column( "def" ) );
collist = {:abc, :def, .}; // <<< added a missing value in position 3
col = As Name( "abc" );// the Right Hand Side evaluates to the expression abc,
// and the expression is assigned to col. The 2nd argument to contains() is
// a name expression, and contains() evaluates that argument. It
// evaluates to missing because there is no subscript to pick a row.
Show( Name Expr( col ), Type( col ), Contains( collist, col ) ); // looks for a missing value
// Name Expr(col) = abc;
// Type(col) = "Number";
// Contains(collist, col) = 3;
colname = "abc";
// contains() evaluates the 2nd argument to a name, but does not
// do another evaluation of the name, so it locates the name
// in the list of names
Show( Type( As Name( colname ) ), Contains( collist, As Name( colname ) ) );
// Type(As Name(colname)) = "Name";
// Contains(collist, As Name(colname)) = 1;
finally,
Show( Name Expr( col ), Type( col ), Contains( collist, Name Expr(col) ) );
does what you want; the 2nd argument to contains() has the nameexpr() wrapper that causes contains() to evaluate it to the name of the expression, abc, not the value.
edit: this is never easy! @hogi points out the head(a+2) example (above) is wrong in Does Head evaluate its argument? ; I think the answer is head() peeks at the argument to see if it is a variable holding an expression, and if not, evaluates it to see if it will produce an expression.
Craige