lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

Two somewhat independent and still vague thoughts:

1. Reading left-to-right and the | args | expr syntax:

I've actually been finding myself wanting a pipe operator that would take the results of one expression and pass them to the next expression. At that level, all it does is allow one to avoid deep nesting. The natural tokens are probably either the | or >>.

Thus in a Unix-ish fashion we could write:

	numbers | filter( odd ) | map( square )

Rather than:

	map( square )( filter( odd ) ( numbers ) )

It would be nice to have the option to pass things in as supplemental arguments, so this syntax still needs work.

On the other hand, with >> it would look like:

	numbers >> filter( odd ) >> map( square )

This may actually be easier to read so I'm not devoted to using the bar token operator for this and in fact >> combines well with the | args | expr syntax:

		>> filter | x | x % 2 == 1
		>> map | x | x * x

Thus, the Unix precedent for piping probably loses to the Smalltalk precedent for argument lists.

The argument for not just using : syntax for pipes is that that requires that the method being sent be supported on the left-hand object and I therefore can't encapsulate a series of pipe operations in another function. For example, using the above, I could define:

	function oddSquares( src ) return src >> filter( odd ) >> map( square ) end

And then write:

	numbers >> oddSquares

I probably can't write:


because numbers almost certainly doesn't have an oddSquares method. Furthermore, the colon syntax also doesn't handle multiple values which the pipe operation would.

Still, as presented, the pipe operation is just syntactic sugar around a function call. Unlike the colon operator it doesn't lead to an underlying specific implementation that might be extensible to deeper semantics. (See the discussions about making the colon operator more first class.) But what if we allowed the pipe operator to do something with metatables on either the left or the right side? Again, I haven't sorted through the semantics but it seems like there might be an opportunity for intelligent pipe building and application.

2. Expression trees

If one wants to get really compact, then we could actually allow function construction at compile time at least for single argument functions. For example, $ isn't currently used as a token, so consider:

	$ would be defined as function( x ) return x end
	$ * $ would be defined as function( x ) return x * x end
	$ + 1 would be defined as function( x ) return x + 1 end

Our example above now becomes:

		>> filter( $ % 2 == 1 )
		>> map( $ * $ )

I will admit that this starts to approach line noise but that's as much for the use of % operator as anything. 

This extension adds complexity to the compiler but not to the runtime since it adds a new expression type representing essentially an expression tree which can be converted to a function if need be. In fact, rather than short hand functions, perhaps what would be really useful is linguistic support for expression trees.

One could almost build this as is, but for the lack of metatable support for certain operators such as and and or. Thus, we can already write:

	Expr.Parm() + 1

This can be runtime compiled to function( x ) return x + 1 end but could also for example be handed to a function to take its derivative.

Expression trees also play out well when trying to implement something like LINQ where we might decide to compile expressions into SQL.

Or, coming full circle, maybe | args | expr isn't short hand for creating functions but rather a syntax for creating expression trees.