Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chain method calls across line breaks #347

Open
hlship opened this issue Oct 29, 2017 · 14 comments
Open

Chain method calls across line breaks #347

hlship opened this issue Oct 29, 2017 · 14 comments

Comments

@hlship
Copy link

hlship commented Oct 29, 2017

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.

@RyanSquared
Copy link
Contributor

RyanSquared commented Oct 30, 2017

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.

@hlship
Copy link
Author

hlship commented Oct 30, 2017

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

@leafo
Copy link
Owner

leafo commented Oct 30, 2017

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

@RyanSquared
Copy link
Contributor

@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.

@hlship
Copy link
Author

hlship commented Oct 30, 2017

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.

@ghost
Copy link

ghost commented Nov 16, 2017

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...

@leafo
Copy link
Owner

leafo commented Nov 16, 2017

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

@vendethiel
Copy link

vendethiel commented Dec 13, 2017

If that's of any interest, this had been an open topic in CoffeeScript 6 years ago ( jashkenas/coffeescript#1407 ) though its fork Coco (satyr/coco#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: jashkenas/coffeescript#3263.

@lifeisafractal
Copy link

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.

@RyanSquared
Copy link
Contributor

@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.

@lifeisafractal
Copy link

lifeisafractal commented Jul 1, 2018

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.

@ghost
Copy link

ghost commented Jul 1, 2018

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.

@ajusa
Copy link

ajusa commented Jun 6, 2020

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.

@natnat-mc
Copy link
Contributor

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants