lua-users home
lua-l archive

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


On Dec 21, 2010, at 10:47 AM, Kevin Vermeer wrote:

> A real-life example of code that would break in a different fashion, that I (and others, I'm sure) have used many times is code like the following line:
> 
> -- Get stdout stream by running "cmd", convert to string, and trim it.
> local cmd = io.popen "cmd":read "*a":gsub("^%s*(.-)%s*$", "%1")
> 
> The current policy allows the ':' operator to work as a pipe between these functions.  The proposed change would, unfortunately, try to call 'read' on the string "cmd" and gsub on the string "*a".  This would require changing such pipes to either break to multiple lines and separate assignments, or add a bunch of parentheses to get the original meaning:

Interesting. I think about piped evaluation on occasion as well since there are a lot of cases where it seems clearer. The problem with all of these is that it only allows piping through methods. So, while we're toying with syntax, how about...

	expr:( fexpr )( args... )

as sugar for:

	local expr_value = expr
	local fn = fexpr
	fn( expr, args... )

Other variants on this idea are possible. I'm not in love with putting the argument list in its own parentheses, but I was trying to avoid adding extra tokens.

Translating the example above assuming no use of methods but allowing for parenthesis-less argument lists we get:

	io.popen "cmd" : ( io.read) "*a" : (string.gsub)("^%s*(.-)%s*$", "%1")

If one can live with "magical" insertion of a first parameter so that the proposed syntax above becomes expr:( fexpr, args... ) then we get:

	io.popen "cmd" : ( io.read, "*a" ) : ( string.gsub, "^%s*(.-)%s*$", "%1" )

This looks odd since it doesn't line up well with the existing calls to these functions.

For functions without arguments, what is really called for is a pipe operator. I'm going to use ">>" here rather than "|" because it seems to read better, but I don't have strong feelings about this.

	x >> math.sin >> math.sqrt

instead of math.sqrt( math.sin( x ) ).

But if we need arguments, then it seems like we need a way to show where the argument goes:

	io.popen "cmd" >> io.read( $, "*a" ) >> string.gsub( $, "^%s*(.-)%s*$", "%1" )

And the next thing we know someone gets to accuse it all of looking Perl-ish.

On the other hand, pipes would probably also work nicely with some form of lambda syntax.

Mark

P.S. Where this is coming up most often for me is that I've been looking at Flapjax inspired event processing chains and building a Lua equivalent.

	eventSourceE:filterE( function( x ) return x % 2 == 0 end ):mapE( function( x ) return x * x end )

The "problem" is that there ends up being an artificial boundary between built-in constructors and client-built constructors. For example, if I find myself wanting to filter on type I can write:

	function typeFilterE( sourceE, targetType )
		return sourceE:filterE( function( x ) return type( x ) == targetType )
	end

But I can't readily add a method from client code (without namespace collision concerns) to let me write:

	eventSourceE:typeFilterE( "string" )

Piping would let me write:

	eventSouceE >> typeFilterE( $, "string" )

Where typeFilterE could remain local or could be imported from a set of add on utilities.

But this then spins off into considering Michael Franz's old Protocol Extension proposal for Oberon and into Objective-C categories...