Ask HN: Your favorite syntactic sugar from any language?

32 points by davegauer ↗ HN
Most programming languages I've encountered, no matter how otherwise unappealing, have at least one syntactic feature that makes them really pleasurable for at least some type of task.

Some things I miss when I don't have them are:

* "=~" for regex matching (as in Perl and Ruby)

* "{foo:['bar','baz']}" for quick data structures (as in JavaScript)

* "for f in *; do echo $f; done" for looping over a 'glob' of files (as in Bash)

What's your favorite bit of sugar?

89 comments

[ 4.1 ms ] story [ 179 ms ] thread
The defaults in Perl, especially the default variable.

(For those not familiar: $_ is the default variable in Perl. Many operations use this variable if you don't specify one. You can use $_ like "it" in English: "Read in a line of input. If it ends in a newline..." In most languages, you would have to deal with "Read it into where?" and "If what ends in a newline?" But in Perl, if you don't specify where to read the line to, it goes into $_, and if you don't specify what you want to see if it ends in a newline, it checks $_, and so on...)

This is worth a bit of explanation, see this link and the code duplicated below:

https://perlmaven.com/the-default-variable-of-perl

I agree with most everything the author says, along with the idea that you shouldn't use it that often.

----------

use strict; use warnings; use v5.10;

while ($_ = <STDIN>) { chomp $_; if ($_ =~ /MATCH/) { say $_; } }

----------

use strict; use warnings; use v5.10;

while (<STDIN>) { chomp; if (/MATCH/) { say; } }

(comment deleted)
Scheme's expression comments are really quite handy, but only possible to implement in languages where everything is an expression.

For those not familiar, you can have the reader just discard an expression, so like (list 1 #;2 3) becomes (list 1 3), but you can handily comment out a giant function call or if expression or something while you work on it.

Scheme's nestable multiline comments are also nice. I'm not sure which language was first, but I think more languages are getting them now (Kotlin has them IIRC).

Pipeline operator ( |> ) in functional programming languages (Elixir, F#, Elm)
I really like python's array slices, a[5:] b[2:3], and list comprehensions, [x * 2 for x in array if x > 0]
Array slices make array/string-parsing coding interviews so much nicer. @.@
So much so, I wish more languages would adopt it.
I love Python comprehensions. I want to dislike Python - significant whitespace, weak lambdas, other trivial complaints - but the comprehensions are so nice, especially combined with yield and the iteration protocol, that it's usually the first language I turn to. There's syntax for sets, dicts, and generators too:

    >>> { char.upper() for char in "hello" }
    set(['H', 'E', 'L', 'O'])
    >>> { char: ord(char) for char in "world" }
    {'d': 100, 'r': 114, 'o': 111, 'w': 119, 'l': 108}
    >>> (char for char in "foobar").next()
    'f'
What I really dislike about comprehensions is that the for syntax comes at the _end_. They would be much easier to parse if it came at the beginning.
I used to feel that way too. When you write out nested for loops the work is at the other end! For what it's worth, it is meant to resemble mathematical set builder notation. I think that familiarity turned me around on it (pun intended).

From that perspective it feels more natural.

[0]:https://en.m.wikipedia.org/wiki/Set-builder_notation

Don't forget negative indexes to wrap around. A[2:-2] trims 2 off each end!
Haskell has way too much syntactic sugar some of the time, but I really appreciate its anonymous function syntax.

If I need a quick-and-dirty map over a list of something, a

  (\x -> x + 1) a
is much nicer than a

  std::for_each(a.start(), a.end(),
   [](float x) {  
            return x + 1;  
        } // end of lambda expression  
   )
or whatnot that I have to do in C++.
You're really looking for `std::transform`. On the other hand,

    for(auto it(a.start()); it < a.end(); ++(*it++));
and

    for(auto &el: a) ++el;
aren't terribly verbose.
JavaScript lets you do:

a.map(x => x + 1)

which is pretty nice. And Rust lets you do:

a.map(|x| x + 1)

C++ syntax is just awful, and especially for functional stuff.

Swift closure syntax, at least in <= 2.0, made for awkward API calls, since it wasn’t clear whether inline or after closures were used.

In Ruby,

    a.map(&:succ)
If you have a named function, rust lets you do

    a.map(succ)
as well, since functions can be used where closures can, as long as the types work.
I'm still surprised Haskell is the only language I've ever seen in which the "add 1 to your argument" function is simply

  (+ 1)
Destructuring in almost any functional language and ES6 JS.
(comment deleted)
Python 3.5+

  >>> first = {"a": 1}
  >>> second = {"b": 2}
  >>> merged = {**x, **y}
  >>> print(merged)
  {"a": 1, "b": 2}
Same exists in ES6:

    const first = { a: 1 };
    const second = { b: 2 };
    const merged = { ...first, ...second };
    console.log(merged) // { a: 1, b: 2 }
Python had it first ;)

  merged = {**first, **second}
Python/Ruby's kwargs, JavaScript's destructuring syntax, Python list comprehensions, Ruby's array push << method, ranges, and literal array expressions
I just discovered being able to use Python to convert a number to a different base:

>>> int(‘x’, y)

...where x is the string/number you want to convert and y is the base.

I haven’t found a practical use case but it’s been a lot of fun to play with.

It's not a syntactic feature, so it can't be syntactic sugar.

Also, it's available in many (most?) other languages: C (strtol), C++ (std::stoi), JavaScript (parseInt), PHP (intval), ...

List comprehension - Python, Haskell.
Beying somewhat of a functinal fanboy i really like Scalas (and oder functinal languages) point free style.

Instead of:

listOfThings.foreach(x => println(x))

You can just type

listOfThings foreach println

Type inference is also great.

Scala in general has lots and lots of syntactic sugar, which is kind of the point of Scala.

Honroable mention:

Rubys &. operator

instead of

y = x.nil? ? nil : x.foo

y = x&.foo

Or just use a language which has some sort of null savety like Option types.

Ruby inline conditionals. They read like English. Short and sweet.

  if condition?
    return
  end

  if !i_probably_shouldnt?
    do_something
  end
Becomes:

  return if condition?

  do_something unless i_probably_shouldnt?
(comment deleted)
When writing one-off Ruby scripts, I especially like using the single-line unless with Kernel.fail:

    fail "bad arg" unless dir = ARGV.shift
(If you could figure out a way to also stuff that line with a transformation to a Pathname for not-nil results, I'd be in heaven.)
Takes a bit of setup to monkey-patch String, but:

    class String
      def to_pn
        Pathname.new(self)
      end
    end
     
    fail "bad arg" unless dir = ARGV.shift&.to_pn
Ruby’s question marks for Boolean functions and exclamation points for mutating ones. I didn’t get t at first, but once I did, I was in love. Perfect for readabiiity.
clojure has this as well and I really like it. Although `!` in clojure usually signifies the function has side effects.
Do you mean like functions named `is_empty?` and `set_value!`? If I've been told right, this comes from Scheme. I first experienced it in Racket, and I absolutely love it. It's seriously one of the greatest conventions.
Do notation in Haskell, Scala etc. Nothing else comes close
You don't need do notation in an imperative language, though.
Do notation can act like an imperative language, but it can also do a lot of things imperative languages can't (e.g. the list monad, or the continuation monad).
You don't need the IO or state monads in other languages, but that's not the only useful monad. Applicative functors with their own do notation are pretty cool too.
I really like C#'s => operator, especially in the context of expression-body members.

  public override string ToString() => $"{fname} {lname}".Trim();
I am overjoyed with newer python's f-strings.

  foo = 5
  bar = 4.5005
  print(f'foo is {foo} and the first bit of bar is {bar:0.2f}')
I really like python's * and * unpacking, and especially that you can do it in the middle with throwaways:

  first, *_, last = function_that_returns_ten_things()
or for building kwarg-only classes from dicts:

  foo = {'x':5,'y':6}
  bar = Point(**foo)
Ruby's & operator.

At first I hated it because of how incomprehensible it was to someone coming from seemingly every other language, but I've started to use it more and more and wishing it existed in other languages.

It's especially useful for when the variable access is many characters. e.g. `if hackerNews.frontPage.votingCircleExists:` with nil checks is `if hackerNews && hackerNews.frontPage && hackerNews.frontPage.votingCircleExists`

and with & operator is: `if hackerNews&.frontPage&.votingCircleExists`.

This is exactly how Python's 'and' and 'or' work too. The first time I saw it I was baffled. It took me a good while to fully wrap my head around it. I grew to love it.

Edit: I was mistaken, it's a little different. This is more like groovys safe navigation ?.

(comment deleted)
Agreed. Null conditional operators are definitely handy in languages that have nulls. C# has this in the form of ?.
This is there piece of C# I'm most envious of missing out on in Java.
I love love love almost everything about Python's syntax.

Slices that support negative indexes. List and dictionary comprehensions are amazing: { (X,Y): X2 + Y for X in range(10) for Y in someiter if Y > 5} I love that they become generators when you use () instead of []. Pure genius. Generators from a function containing yield or yield from equally beautiful. % for string interpolation was so good they had to bring it back. Tripple quotes using """ and ''' are hard to beat. Using func(list) to unpack a list into a functions arguments. Or func(dict) to unpack a map by name. Using args and kwargs to implimentaions variadic functions. I love indentation based code blocks. The way 'and' and 'or' can return values is so much better than a ?: terinary. I think I dumped a third of the Python spec there.

I could use a multi-line lambda but it's not a huge omission. Pipelines like bash | pipe or F# |> would be welcome.

go's select statement is also subtle but smart. Doing multiple waits in one thread is tricky to impossible in most any other language.

Built in regex like perl is always nice when you have it.

I'm sure I missed something gushing over Python, but tapping this out over the phone if already killing my thumb.

Java desperately needs groovys's safe navigation operator a?.getB()?.getC(). It's like a normal method call but returns null if the receiver is null instead of throwing an NPE.

I love Python too (a lot), but I disagree with some of these.

> I love that they become generators when you use () instead of [].

I kind of like this, and I kind of hate it. My second-favorite language is Haskell, where everything is lazy, so to me it almost seems like everything should be generators anyway (except when a value is expected).

> % for string interpolation was so good they had to bring it back.

I hate % for string interpolation. It's ugly. Why do my variables need "variables" in the strings? It's indirect interpolation. I far prefer f-format strings, because `print(f"Correct: {correct}/{total}")` reads much better.

> Tripple quotes using """ and ''' are hard to beat.

Unconvinced. I feel like the syntax for multiline strings could be a bit simplified.

Also, it's a pain to combine multiline strings with indentation. I don't want all the extra whitespace at the fronts of the lines, but I also don't want to process the text immediately afterwards... sigh.

But I totally agree with the other points. Overall, the syntactic sugar is phenomenal! Just these points I disagree about.

Eager lists have their place! Ya can't sort or slice into a generator. I like the choice.

I'm mostly still in Python 2.7 land so I forgot about the new f strings. I've never used them and I'm 50:50 only because I can do "%s %s %s" % (a.b(), z[:4], x2) without binding new variables. Can f strings do that? I don't know.

I agree about the whitespace issues. Have you seen a language do them better? I proposed using | to prefix each line for Java's string literals. At least they're explicit that way. I'm just glad Python used """ instead of backticks or or <<<START style here docs.

> Eager lists have their place! Ya can't sort or slice into a generator. I like the choice.

Something like that could be handled implicitly... hmm but that seems bad. Maybe you're right that having a choice is better!

> I can do "%s %s %s" % (a.b(), z[:4], x2) without binding new variables. Can f strings do that? I don't know.

In 3.x f-string style, `f"{a.b()} {z[:4]} {x2}"` would work just fine! Python just expects any expression inside the curly braces, so as long as you give up a value (and that value is either a string or an object of a class that implements `__str__`, I think), you're golden.

It's particularly useful when you have a lot of interpolation to do. The more arguments there are, the more tedious it is to read through a %-style format and figure out which variables correspond to what markers in the string.

> I agree about the whitespace issues. Have you seen a language do them better?

Not really, unfortunately.

I think a case could be made that all indentation should be based on either (a) the triple-quote or (b) the first non-whitespace character of the first line. But I don't know if that's intuitive enough. Definitely something to think about, though!

My favorite from python are decorators, especially the built-in ones like @property. They can improve readability immensely, so the don't feel extraneous.
> groovys's safe navigation operator a?.getB()?.getC()

Apache Groovy also has shortcut property syntax so you can write `a?.b?.c` to get the same effect. Of course, all the syntactic sugar mixed together becomes quite confusing quite quickly. Some Groovers want to write the `?` once only and have it ripple through so you'd only need write `a?.b.c` in Groovy 3. But then, what if you actually want an NPE if `getB()` works but `getC()` doesn't? Perhaps `a?.b¿.c` would do the trick!

Syntactic sugar is not another name for "really cool syntax". They are non-essential constructs, which could be removed without much effect on the language, except for readability.

"=~" in Perl and globbing in shell are not syntactic sugar. They are fundamental parts of the languages.

To follow on from this, "syntactic sugar" is a permitted short-hand that can be losslessly transformed into another, often more verbose construction. This is why it must, definitionally, add no new features.

For example, list comprehensions in Python: `[x^2 for x in L if x < 10]` is EXACTLY the same as `map(lambda x:x^2, filter(lambda x: x<10, L))`. The list comprehension is just easier on the eyes.

> `[x^2 for x in l if x < 10]` is EXACTLY the same as `map(lambda x:x^2, filter(lambda x: x<10, l))`

Except the former resolves to an actual list value, whereas the latter is a generator. (You would've been right if you'd used the generator syntax () instead of the list comprehension syntax []. Just thought I'd point it out since this whole thread is about syntax.)

That's true for only python 3.
The other commenter didn't specify, which led me to believe it was a (simple) mistake on their part. You're not wrong, but it seems to me that dragging out this phase of "Well Python 2 had X but Python 3 has Y" will not help people move to all being on 3. (And, with some exceptions, everyone should really be moving to 3.)
> Except the former resolves to an actual list value, whereas the latter is a generator.

The latter is an actual list value if you're still using Python 2:

  pi@raspberrypi ~ $ python
  Python 2.7.3 (default, Nov 24 2017, 21:13:24)
  [GCC 4.6.3] on linux2
  Type "help", "copyright", "credits" or "license" for more information.
  >>> map(lambda x: x^2, filter(lambda x: x < 10, [1,2,3]))
  [3, 0, 1]
  >>>
  pi@raspberrypi ~ $ python3
  Python 3.2.3 (default, Mar 25 2017, 13:24:50)
  [GCC 4.6.3] on linux2
  Type "help", "copyright", "credits" or "license" for more information.
  >>> map(lambda x: x^2, filter(lambda x: x < 10, [1,2,3]))    
  <map object at 0x76a72570>
Right, you're quite correct! I assumed OP was using Python 3 since that's the current version, and their statement would have been true in 3 with the small change I suggested. Their point is still correct overall, but if they're going to make an example from an older version of the language I think they ought to specify as much.
Python 3 is a different language :)
Ah, good point! I did mean "really cool" syntax in any of its glorious forms whether they be elegant core constructs or sugary shorthand.

A followup question to honor the important distinction: _do our favorite bits of beloved syntax tend to be core language or true sugar?_

I suspect core, since it would tend to be most well integrated. But perhaps sugar since it is created for the sole purpose of legibility and joy.

Ruby's ||=

`a ||= b` sets a to b, unless a is already defined.

async/await

I see async/await as the only way to write asynchronous code that's readable

range() in Python

[foo, bar, ...baz] ES6 Destructuring