moonscript icon indicating copy to clipboard operation
moonscript copied to clipboard

Chain method calls across line breaks

Open hlship opened this issue 8 years ago • 14 comments

I'd like to be able to chain method calls across line breaks. e.g.

flux.to(cell, 0.5, {dy: 0}) 
\ease("quintin") 
\delay(0.05 * (cell.column - 8))

But nothing I've found works. I'd be open to indenting the second and third lines.

Depending on indentation, I get Compile error: Short-dot syntax must be called within a with block or Failed to parse: [2] >> \ease("quintin").

It works as expected if it is all one line, and there are no spaces before the backslashes.

hlship avatar Oct 29 '17 17:10 hlship

If you don't need to worry about keeping the value, I think you can do something like:

with flux.to cell, 0.5, dy: 0
  \ease "quintin"
  \delay 0.05 * (cell.column - 8)

This might not work as expected because it stores the flux.to value as a variable then applies the method calls to that rather than literally chaining them.

RyanSquared avatar Oct 30 '17 03:10 RyanSquared

Is that a temporary work around, or a statement that the syntax I want will not / can not be implemented?

hlship avatar Oct 30 '17 16:10 hlship

The syntax you suggested with \ at the beginning of the next line would conflict with the with block syntax. We could implement this by having the \ be a trailing character on the proceeding line

leafo avatar Oct 30 '17 16:10 leafo

@hlship It's not a workaround, it's how the language was designed. The syntax you were using is already implemented, but with something else. It might work for your use case though depending on what \delay() affects - if it affects a value returned from \ease() which is not a flux.to value, then it won't work.

RyanSquared avatar Oct 30 '17 16:10 RyanSquared

I'm just used to Clojure where everything is chaining operations (because all data is immutable). But I do like MoonScript, far more fluid, concise, and readable than standard Lua.

I did a bit of work in CoffeeScript a couple of years back, so this is familiar territory.

hlship avatar Oct 30 '17 17:10 hlship

We could implement this by having the \ be a trailing character on the proceeding line

Suppose we're using some 'chainy' API like LuaLinq's one. This is an artifical example:

from(array)\ -- I know 'from' is a reserved word, but this is just an example
where((item) ->
  item.startdate > date1 and item.startdate < date2)\
select((item) -> item.taskname)\
toArray!

Without these parentheses around arguments it could look weird. With them it looks more 'heavy' than Moonscript's parentheses-less style, but still more comfortable than Lua syntax. Would be a nice feature in Moonscript. Also, what should happen to indentation with each method call? There is no difference between 1st call and any other call, so it looks like it should either stay the same (then it's easy to not notice \ at the end of previous line) or increase at each call...

ghost avatar Nov 16 '17 12:11 ghost

That's a good point @Penguinum about not being able to leave out parens with that style. Regarding white-space, since there's a character signifying the next line there doesn't need to be strict white-space, so I would expect people to indent

leafo avatar Nov 16 '17 15:11 leafo

If that's of any interest, this had been an open topic in CoffeeScript 6 years ago ( https://github.com/jashkenas/coffeescript/issues/1407 ) though its fork Coco (https://github.com/satyr/coco/issues/64) and LiveScript added a space-before rule where a 'b' .c is a('b').c. This was also added to CoffeeScript a few years back: https://github.com/jashkenas/coffeescript/pull/3263.

vendethiel avatar Dec 13 '17 23:12 vendethiel

Any update on this one? I hit on this when using argparse (https://github.com/mpeterv/argparse). That makes use of function chaining much like the python argpase. You can very quickly end up with cumbersome lines out of this when you add many options to a single argument. Would love an elegant solution to this one.

lifeisafractal avatar Jun 28 '18 21:06 lifeisafractal

@lifeisafractal can you provide an example? Unfortunately chaining after-line is too close to the with format but it'd be interesting to see if I could redo it with the with syntax.

RyanSquared avatar Jun 30 '18 17:06 RyanSquared

I'm pretty new to Moonscript, so it turns out in the case of argparse, with is exactly what I wanted. This works because the argparse objects are mutable. This long line:

install\option("-f --file", "file to install")\args(1)\count(1)\argname('UPDATE_FILE')\convert(io.open)

can become this using the with statement which is arguably much more readable.

with install\option("-f --file", "file to install")
	\args(1)
	\count(1)
	\argname('UPDATE_FILE')
	\convert(io.open)

Where this falls down is when working on immutable objects like strings.

my_str = my_str\trim()\split('\n')

Is not functionally equivalent to

my_str = with my_str
	\trim()
	\split('\n')

Not really a big deal I guess because you can use more lines. IMHO this is a bit more verbose, but not the end of the world by any stretch.

my_str = my_str\trim()
my_str = my_str\split()

Just thinking out loud here, it might be cool to have another variant of with where rather than applying each line in the block to the with argument, it truly chains them. For the sake of argument let's call this chain. so:

my_str = " fOO\nbAr  "
my_str = chain my_str
	\lrim()
	\rtrim()
	\lower()
	\split('n')

would emit the following Lua

local my_str = " fOO\nbAr  "
do
	local _chain_0 = my_str
	_chain_0 = _chain_0:ltrim()
	_chain_0 = _chain_0:rtrim()
	_chain_0 = _chain_0:lower()
	_chain_0 = _chain_0:split('\n')
	my_str = _chain_0
end

This would replace the following (in my opinion) more verbose code.

my_str = " fOO\nbAr  "
my_str = my_str\ltrim()
my_str = my_str\rtrim()
my_str = my_str\lower()
my_str = my_str\split('n')

This is just a half-baked thought so don't take it too seriously. Overall it's likely better to keep the language simpler and not add mountains of syntax sugar.

lifeisafractal avatar Jul 01 '18 15:07 lifeisafractal

BTW chaining on multiple lines is possible if you leave method call on the line it should [currently] be but move arguments and closing parentheses on new line:

some_string = another_string\gsub(
  "asdf", "qwer"
)\gsub(
  "qwer", "asdf"
)\upper!

Kind of more ugly, but works.

ghost avatar Jul 01 '18 16:07 ghost

Found this issue today when I was trying to use underscore.lua. Anyone else have any workarounds two years later? Normal lua supports multi-line chains, so it would be really nice if I could pull the same thing off in moonscript without temporary variables.

ajusa avatar Jun 06 '20 01:06 ajusa

@ajusa told me to put it here, so here goes (discord thread)

wrap = =>
	setmetatable {_val: @},
		__index: (k) =>
			(_, ...) ->
				@_val=@_val[k] @_val, ...

unwrap = =>
	@_val

_ = require 'underscore'

with wrap _{1, 2, 3, 4, -2, 3}
	\chain!
	\map => 2*@
	\filter => @>3
	\each => print @

a=unwrap with wrap _{1, 2, 3, 4, -2, 3}
	\chain!
	\map => 2*@
	\filter => @>3
	\value!

print table.concat a, '\t'

the wrap function creates an object that automatically wraps methods and mutates its internal state. unwrap just undoes it so you can collect it after the with call without having to add another line.

note that this is an ugly hack and another way to do this would be way cleaner

natnat-mc avatar Jun 06 '20 16:06 natnat-mc