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

Lightweight lambda syntax #129

Open
copumpkin opened this issue Mar 14, 2016 · 13 comments
Open

Lightweight lambda syntax #129

copumpkin opened this issue Mar 14, 2016 · 13 comments

Comments

@copumpkin
Copy link

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):

  • Scala: (arg1, arg2) => body (probably requires nontrivial lookahead and complicates the parser, but is pretty appealing from a human standpoint)
  • Haskell: \arg1 arg2 -> body (all functions are unary, but this is a shorthand for two lambdas)
  • Nix: arg: body (all functions are unary)
@sparkprime
Copy link
Collaborator

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
Copy link
Author

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 😄

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
Copy link
Collaborator

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
Copy link
Author

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 😄). 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
Copy link
Collaborator

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

@sparkprime
Copy link
Collaborator

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
Copy link
Author

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
Copy link
Contributor

benley commented Mar 31, 2016

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
Copy link
Member

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
Copy link
Collaborator

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
Copy link
Author

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

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
Copy link
Contributor

mikedanese commented Jul 26, 2016

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
Copy link
Collaborator

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.

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

No branches or pull requests

5 participants