google / jsonnet

Jsonnet - The data templating language
http://jsonnet.org
Apache License 2.0
6.92k stars 437 forks source link

Lightweight lambda syntax #129

Open copumpkin opened 8 years ago

copumpkin commented 8 years ago

One of jsonnet's big selling points is being able to write functions and pass them around as values. Can we have a more lightweight syntax for anonymous functions than the function (args) ... one? I catch myself deliberately trying to to avoid writing them due to the syntactic noise.

I don't really have a proposal for the syntax I want, but here are a few lightweight ones (not sure how compatible they are with your current grammar):

sparkprime commented 8 years ago

Having more than one such syntax would be weird, and removing the existing one would break compatibility, so I think this ship has sailed now.

copumpkin commented 8 years ago

What would be weird about it? I can't see much downside other than a sense of desugaring cleanliness/redundancy.

Scala basically has two syntaxes for lambdas. Although now that I mention it, I don't really like the other one :smile:

A broader question: do you consider jsonnet to be roughly "set in stone" at this point? I assumed that the 0. leading version number meant you were still open to significant change, but might be reading too much into that.

sparkprime commented 8 years ago

Backwards compatible changes are fine at any time as long as I like the cost / benefit ratio :)

The cost is complexity -- in people making their own implementations, and complexity to learn / document the language.

In this case the complexity is not much but the benefit is not so much either. You could sway my opinion on that if you showed me some real configs that would have real benefits from such a feature.

Before 1.0 even backwards incompatible changes are OK but there would have to be a really good reason. Like after launch (2014 time) I changed the meaning of : -- that was a significant change. Such a change now, when we have many users would be unthinkable. However a few months ago I removed the use of super in contexts other than super.f and super['f']. I also required import "foo".f to have parens like (import "foo").f. And a few weeks ago, I required :: to be adjacent, not allowing : /* */ : (for example). But these were cases where I judged (admittedly without much evidence) that probably nobody would be affected, so the benefits outweighed the cost.

copumpkin commented 8 years ago

Ah, I see. The only benefit, as I see it, is the ability to do far more "conventional FP" stuff. As soon as I started doing nontrivial things with jsonnet, I basically wrote a big swath of higher-order functions over objects (think of them as dictionaries, I guess) because I was getting frustrated with object comprehensions and needed to be able to manipulate mappings in ways that don't translate nicely to comprehensions (was also getting sick of seeing objectFields all over my code :smile:). What this means in practice is that I have lots of HOFs with lots of function (foo) someexpr in them and I find the function part fairly distracting. Definitely not a "slam dunk" argument, but in general I like to err on the side of "make it as easy as possible to factor common patterns out" and doing that today requires a decent amount of the word function appearing in my code.

Having said all that, it's a syntactic issue and is nowhere close to a dealbreaker. It would just simplify a certain coding style, is all.

sparkprime commented 8 years ago

What's the output of bc -l <<< "$(sed 's/function/L/g' < myfile.jsonnet | wc -c) / $(wc -c < myfile.jsonnet)"

sparkprime commented 8 years ago

FWIW the requirement to use std.objectFields in object comprehensions was an opinion born out of conservatism so it could easily be challenged by sufficient evidence that this is actually a bad idea :)

copumpkin commented 8 years ago

Okay, so I'm probably overstating the severity somewhat! but this file is also currently mostly "data", along with a few functions to process it:

$ bc -l <<< "$(sed 's/function/L/g' < whoa.jsonnet | wc -c) / $(wc -c < whoa.jsonnet)"
.98033891957681864993

To put it differently, it's 30 instances of the word function in a 200ish-line file. Not oppressive, but not my favorite either!

benley commented 8 years ago

I certainly wouldn't mind a shorter form of the function(x) x form - perhaps just fn(x) x - but it's also not a showstopper by any means.

davidzchen commented 8 years ago

Seems that the chief complaint here is that the function keyword is a bit long. If that's the case, then perhaps we can have a shorter keyword, such as fn as in Rust and as @benley suggested, fun as in OCaml, func as in Go, or def as in Python.

sparkprime commented 8 years ago

fn would be my preference, but this would not be a backwards compatible change as introducing a keyword would break variables named the same thing. So the value would have to be pretty high to justify it.

copumpkin commented 8 years ago

A nice juicy piece of punctuation syntax wouldn't have to overlap with anything :smile:

But seriously, this isn't really a huge deal. I just think functions are a beautiful thing and want to use more of them. It's not even writing function that bothers me; it's just that it makes otherwise pretty code look cluttered. 100% aesthetic, I admit!

mikedanese commented 8 years ago

With jsonnet fmt we could migrate existing code from function(x) to fn(x) or (x) => pretty painlessly. Jsonnet fmt could rewrite the deprecated form for a release or two before we remove the old syntax. Jsonnet is still pre 1.0 so backwards incompatible language changes aren't terrible, especially if there is a way to migrate code automatically.

sparkprime commented 8 years ago

I don't have a sense on whether people would be prepared to trust jsonnet fmt. I'd like to think so :)

fn() would need something like jsonnet fmt because of renaming local variables called "fn" to "fn" without treading on existing variables that may be called fn.

On the other hand () => could probably be done with a script that understands string literals and comments, and nothing else. Such a script would preserve all other formatting, which jsonnet fmt does not.