Hacker News new | ask | show | jobs
by ram_rar 1739 days ago
I love new programming languages and the concepts they bring to the table. But I feel PL designers should think about Dev experience.

Is it just me or is the "\" in front of parenthesis (func params) is just off-putting. I wonder, what purpose does it solve?

Familiar syntax makes it much easier to adopt and easily convince devs in your team to try it for real.

5 comments

I agree, but lambdas do not really have a canonical syntax in the same way that braces have become synonymous with delimiters for statement blocks. This particular syntax comes from Haskell, where e.g.

    \x -> f x
denotes an anonymous function that applies its first argument to `f`.

But I have seen at least these forms in other languages of varying popularity:

    [x] f x

    |x| f x

    { f(it) }

    { x -> f(x) }

    fn x => f x

    function(x) { return f(x); }

    x -> f(x)

    (x: A) => f(x)
In other words, I think it is really hard to pick a syntax for this construct that every programmer is going to feel familiar with, especially if your language is supposed to cater both to programmers coming from FP and more traditional languages.

Edit: Fixed error in JS example; added Java and Scala.

Examples 3 and 4 are cursed, to my eye. Curly braces denote a block, so why is there some magic syntax inside the block that denotes that this block happens to be a lambda? surely the block is the closure itself. The syntax to say what the block is goes outside.

Imagine if i wrote a conditional like that:

    if x > y { return x; } else { return y; }
as

    { if x > y; return x; } { else; return y; }
It completely erases the usefulness of {}, and is cursed, cursed I say! And I’m looking at you, rust, swift, ruby, etc.
I wholeheartedly agree. Whether {} denotes a block or a lambda is context-dependent in the language I took these examples from (Kotlin). I think they adopted the syntax from Groovy. It is "useful" for creating DSLs because you can implement constructs like `.forEach` so they look like imperative constructs, e.g.

    list.forEach { x -> ... }
But it becomes harder to read code outside an IDE, especially if the implicit `it` construct is used.
That's the thing that annoys me most about Kotlin (even though aside from that it is my personal 10X language). In a large project that uses DSL's, coroutines and flow it quickly becomes a curly brace mess and it gets hard to see what code runs where, when and in which context.
Same, I use Kotlin daily, and I would choose it over Java any day as it really solves a lot of its quirks very nicely. There are however some parts of the syntax that keep confusing me on a daily basis, curly braces being one of them. The other is the weird unintuitive asymmetry that is going on between function types and lambdas. For example, a lambda of type

    (Int, Float) -> Boolean
is written as

    { i, f -> ... body ... }
but a lambda of type

    (Pair<Int, Float>) -> Boolean
is written as

    { (i, f) -> ... body ... }
because `(x, y)` is a destructuring pattern that projects the first and second components into `x` and `y`, respectively. The pattern in the lambda has to use tuple syntax exactly when the type doesn't, and vice versa. Gets me on a weekly basis even though I completely understand what's going on, it's just not very ergonomic.
No, the idea is that {} denotes executable blocks, which lamdas are. In Kotlin, if the last parameter of a function is a lambda, you can close the parenthesis before the lambda:

  f(5, { square(it) }
can be written as

  f(5) { square(it) }
which makes constructs like

  if(condition) { foo() }
Look like

  if(condition, { foo() } )
as in a function that takes a boolean and a lambda and only executes the lambda if the boolean is true.

It's a neat reinterpretation of what {} means.

I’m not super familiar with kotlin, but swift does the same thing you’re describing so i get where it’s useful. My beef is with including metadata about the executable block (parameter names) inside the block. So per your example:

    f(5) { square(it) }
My preference would be to tag the fn signature on the outside of the braces, something like:

     f(5) [(x)->int]{ square(x) }
I’d also accept ||, (), or nothing as the delimiters around the fn signature. Key point is that it’s OUTSIDE the executable block.

I’m not opposed to using some smarts to infer/simplify the expression when possible. I.e. if it’s a closure with inferable parameter and return types the “it” construct could be used (in swift they use $0, $1, $2 etc for unnamed parameters). Just the only thing inside an executable block {} should be code that gets executed - not type information about that block

I can’t believe the canonical anonymous function notation is omitted:

    (lambda (x) (f x))
On the other hand, this is a discussion about syntax, and Lisp is the “syntaxless” language…
How is that canonical? λ x . f x is the original syntax of Church, Lisp came around decades later.
Canonical from a programming languages perspective.
> function(x) { f(x); }

Slight correction (presuming JavaScript): that’d be `function(x) { return f(x); }`.

This could also be R, which allows for `function(x) {f(x)}` and (since version 4.1) `\(x) {f(x)}`.
Thank you, fixed!
for clojure there's

    #(f %)
or

    (fn [x] (f x))
This is canonical syntax in any functional programming language. This is like a Haskell programmer complaining that in most post-haskell languages, the keyword class means an oop type instead a class of types.
No it's not. It's used mostly by Haskell, but ML/SML/OCaml and Scala use a different syntax.
All true, but perhaps it’s better to use something more familiar to more developers, such as

    x => f x
Or

    x -> f x
Passerine[0] uses the latter syntax.

[0]: https://github.com/vrtbl/passerine

Probably looks like lambda. Haskell has them.

    \x -> x + 1
“λ” is a bit inconvenient to type on most keyboards. And supporting Unicode at the source code level is still surprisingly rare in programming languages.
By that logic we would never get new programming language ideas, because we could only reuse what has already been done.