Hacker News new | past | comments | ask | show | jobs | submit login

This is the first justification of not using Rust that I actually agree with. Well written.

I recommend reading Roc's FAQ too - it's got some really great points. E.g. I'm internally screaming YESSS! to this: https://www.roc-lang.org/faq.html#curried-functions

But then it has other weird features too, like they seem to be really emphasising "friendliness" (great!) but then it has weird syntax like `\` for anonymous functions (I dunno where that dumb syntax came from by Nix also uses it and it's pretty awful). Omitting brackets and commas for function calls is also a bad decision if you care about friendliness. I have yet to find a language where that doesn't make the code harder to read and understand.




The syntax came from Elm, which got it’s syntax from Haskell (where Nix also got it from) which got its syntax from ML.

It’s a syntax that’s several decades old at this point.

It’s different, but not harder. If you learned ML first, you’d found Algol/C-like syntax equally strange.


(ETA: speaking strictly about anonymous functions; on rereading you might be talking about the absence of parens and commas for function application.)

That's not ML syntax. Haskell got it from Miranda, I guess?

In SML you use the `fn` keyword to create an anonymous function; in Ocaml, it's `fun` instead.


I believe the `\` character for functions is original to Haskell. Miranda does not have anonymous functions as a part of the language.


The \ is a simplified lambda, because most programmers can't type λ easily.


I would be very in favour of making λ a keyword, though. Maybe a linter could convert \ to λ.


it would be confusing since it's not part of UnicodeSyntax

https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/unic...


Well, even better to add it to that! But I was thinking generally, to be honest. I really want Python to synonimise the keyword lamdba with the λ symbol so I can golf my Advent of Code code better.


Argh. Synonymise.


Well, ML (or at least the first versions of it) used a λx • x syntax [1] for λ-abstractions, the same (excluding the use of • over .) notation as used with the Lambda Calculus, and I've always assumed \ was an ASCII stand in.

[1]: https://homepages.inf.ed.ac.uk/wadler/papers/papers-we-love/... (can be spotted on page 353)


That paper isn't showing real ML syntax itself; it's a mathematical presentation to demonstrate how the type system algorithm works. The actual original LCF/ML syntax would differ. I don't believe it used an actual lambda character, although for the life of me I can't find any evidence one way or another, not even in the LCF source code (https://github.com/theoremprover-museum/LCF77)

But yes, the slash is just an ASCII stand-in for a lambda.

ETA: I tracked down a copy of the Edinburgh LCF text and I have to eat crow. It doesn't use a lambda, but it does use a slash rather than a reserved word. The syntax, per page 22, is in fact, `\x. e`. Similar to Haskell's, but with a dot instead of an arrow.

https://archive.org/details/edinburghlcfmech0000gord


Well if you're going to eat crow, I may as well eat pigeon, for I didn't realise that paper wasn't showing real source.

Thanks for the link to the LCF text though :^)


They recently changed the syntax to add parens, commas and use `|arg|` for closures :)

https://github.com/roc-lang/roc/releases/tag/0.0.0-alpha2-ro...


Are the Roc people really doing everything they can to ruin everything they had going for them? Appealing to those who know nothing and won't be willing to touch anything FP at the cost of annoying those who actually want to try it is just so stupid


I say this as someone who enjoys reading Rust more than Haskell or Elm -- that looks like a really bad idea for aesthetic reasons anyway. I mean if you want the syntax to look like Zig or Rust, perhaps go all the way there instead of making a kind of a mutant hybrid like this. Syntax is superficial and the semantics actually matter, but that doesn't mean the syntax can be just anything.

Is there some deeper technical reason for making such changes?


Boooo


oh wow it went from being a very clear language to looking more like a hodgepodge of a few different languages.


Ah great improvements! I don't know why the sibling comments are so negative; this is clearly better.


Why? I'm used to \ meaning lambda. I understand you aren't, and that's fine. But it's just your weird opinion determined by what programming languages you've learned.

Ultimately, it's just syntax and not so important. Semantics are important.


> Why? I'm used to \ meaning lambda.

1. I know what lambda calculus is, and I didn't even make the connection between \ and lambda. It's pretty tenuous.

2. Most programmers do not know what lambda calculus is. This is supposed to be a friendly language, not an obscure academic one.

3. It's not even the same lambda as in lambda calculus, e.g. it takes multiple arguments.

4. Lambda was a reasonable choice in lambda calculus since it's a very "mathsy" language, and it's pretty much the only symbol in the language. It's a pretty awful choice for a practical programming language though - there's a reason 99% of languages use something like `fn` or `fun` or `function` or `def` instead of lambda to define top-level functions.

I'm not a huge fan of `|foo|` either to be honest - I don't see why you can't simply use the same syntax for anonymous and named functions - but it is at least a little better.

If it were up to me I'd go with something like

  fn double(a: list[int]) -> list[int] {
    a.map(fn(i) { i * 2 })
  }
Same syntax; just allow omitting the name and inferring the types. I can't recall any languages that do that though so maybe there's some tricky reason it can't work?


Right now I’m in between liking Gleam or Roc for a hobby functional language. The syntax you wrote is Gleam[1], well aside from it being List(). It’s very approachable, very consistent and that’s a core goal.

Roc was appealing because it was slightly more distinct and more unique. Plus it was more focused on speed. I appreciate them wanting to become more approachable. The new syntax seems less approachable to me because it’s now a half step between approachable Haskell and what feels like a combination of Ruby and Python.

[1]: https://gleam.run/


Roc does use the same syntax for anonymous and named functions, both before and after the recent syntax changes.


I think because a closure is not a function as a closure can capture its environment, in a way that functions can't, so using the same syntax for closures and functions can be misleading (even though languages like JS do it)


I dunno, I could say that functions do capture their environment (global variables are captured by reference).

I don't really see how it would be misleading.


> Most programmers do not know what lambda calculus is

I would frankly be shocked if a 4 year syllabus in CS these days just skipped any mention of Alonzo Church, especially considering that it's fundamental to functional programming and theoretical computer science...

Maybe it's not taught in "CS" programs that are actually just "practical software engineering" bootcamps with extra steps. The real nerds know about lambdas. They're even literally called that in Ruby (and possibly other languages).


FYI: I have a computer science degree. I never learned any functional programming, nor lambda calculus during university. I learned about both topics in my late 30s as a professional.


I find this insane, considering how important I find it now.

Honestly, I didn't take enough CS to get into functional langs, but I did learn what a lambda was. The importance of it all came later, after years of struggling with bugs in million-line codebases that wouldn't have even existed in an immutable, functional language without inheritance.


Your school should lose its accreditation. Your programme is an absolute failure and sounds like they called their software engineering programme computer science


Brace yourself... most programmers don't have a CS degree.

Yes really.


Depends on where they are on the globe.

In many places that won't get them through HR, nor calling oneself Software Engineer is legally allowed without a corresponding degree.


It cannot be


There can't be a more democratized language than Python and in Python closures are defined literally using lambda keyword


I guarantee that 99% of Python programmers just think that's a weird way of saying an anonymous function.

Hell I used lambda functions in C++ for years without knowing what lambda calculus is. And C++ doesn't even use `lambda`.


Sure, but that also means this kind of syntax does not alienate people just because they don't know what lambda calculus is. And they will appreciate it even more when one day they get curious and look up why it is called a lambda


Pretty much all of those changes look bad to me.


Jesus, why? This is a bummer.


I feel that he got a lot of pressure from the FP community and wrote a bunch of nonsense instead of being straightforward with them.

The only relevant reason he lists is point-free, but he doesn't go far enough. Point-free very often turns into write-only balls of unmaintainable nastiness. Wanting to discourage this behavior is a perfectly reasonable position. Unfortunately, this one true argument is given the most tepid treatment of all the reasons.

Everything else doesn't hold water.

As he knows way better than most, Elm has auto-curry and has been the inspiration for several other languages getting better error messages.

Any language with higher-order functions can give a function as a result and if you haven't read the docs or checked the type, you won't expect it. He left higher-order function in, so even he doesn't really believe this complaint.

The argument about currying and pipe isn't really true. The pipe is static syntax known to the compiler at compile time. You could just decide that the left argument is applied/curried to the function before the right argument.

I particularly hate the learning curve argument. Lots of great and necessary things are hard to learn. The only question is a value judgement about if the learning is worth the payoff. I'd guess that most of the Roc users already learned about currying with a more popular FP language before every looking at Roc, so I don't think this argument really applies here (though I wouldn't really care if he still believed it wasn't worth the learning payoff for the fraction of remaining users).

To reiterate, I agree with his conclusion to exclude currying, but I wish he were more straightforward with his one good answer that would tick off a lot of FP users rather than resorting to a ton of strawman arguments.


No trolling/nitpicking from me: You wrote <<The only relevant reason he lists is point-free>>. What do you mean by "point-free"... or did you write "point three" and it was auto-corrected on a mobile phone?

I also tried Googling for that term (never heard before), and I found these:

    https://stackoverflow.com/questions/944446/what-is-point-free-style-in-functional-programming

    https://en.wikipedia.org/wiki/Tacit_programming
If you really meant "point-free", can you tell me where in his post he mentions it? I would like to learn more.


Point-free is a functional programming style where you avoid assignments and prefer to do everything as a group of nested, mapped, and composed functions. It relies heavily on partial application. Like everything, it can be good when used in moderation.

Here's a made-up JS example. I've attempted to be fair to both approaches doing it how I personally would with each.

    //typical implementation
    const howManyAdults = (data) => {
      const adults = data
        .flatMap(obj => obj.type === 'parent' ? [obj, ...obj.children] : obj)
        .filter(obj => typeof obj.age === 'number')
        .filter(obj => obj.age >= 18 && obj.age < 150)

      adults.forEach(adult =>
        console.log(`${adult.name} is an adult age ${adult.age}`)
      )

      return adults.length
    }


    //nice "point-free" implementation using helper functions
    const transformData = obj => obj.type === 'parent' ? [obj, ...obj.children] : [obj]
    const isAdult = obj => obj.age >= 18 && obj.age < 130

    const howManyAdults = pipe(
      flatMap(transformData),
      filter(hasTypeOf('age', 'number'),
      filter(isAdult),
      logEach`${pick('name')} is an adult age ${pick('age')}`,
      len,
    )


    //completely point-free gets crazy
    const howManyAdults = pipe(
      flatMap(ifElse(
        compose(eq('parent'), pick('type')), 
        juxt([identity, pick('children']),
        identity,
      ), []),
      filter(hasTypeOf('age', 'number'),
      filter(both(gte(pick('age',18), lt(pick('age'), 130)))),
      logEach`${pick('name')} is an adult age ${pick('age')}`,
      len,
    )
I went through a point-free phase early in my career, but even with lots of practice, I can't believe that most devs ever find the third example as readable as the first or second. I'd also note that this is a trivial example and doesn't require you to track any monad wrappers either.

Personally, I rather like reading the moderate middle example because it removes the boilerplate and allows me to easily follow the overall flow without getting caught up in the details. But I'll take the first example every time if it means never dealing with the third example.


inb4 pointless programming is pointless


It's in the FAQ. Someone else posted a link to it in the comments, check it out. And yes, it is about tacit programming.


It's the closest you get to 'λ' on a US keyboard.


So this assumes knowledge of an obscure theoretical programming language, and a dubious symbol replacement. Yeah...


It doesn't assume it, it's syntax, you can just use it without knowing where it comes from.

It's like english. You don't need knowledge in obscure proto-germanic linguistics to use articles in your sentences. But if you want to understand why we seem to randomly attach "a" before nouns— proto-germanic linguistics has the answer (probably, I just speak english I don't know where all its syntax originates).


Functional programming is common in university curriculum. Terrifying that it can still be considered obscure.


IIRC, Richard explain that in one of his videos about Roc. I have seen at least a handful of them.


I believe Haskell uses it as well.


my hot take: the language should accept \, but formatters should replace it with λ


Lean does symbols very well. You can just type \r and it will replace it with a right arrow as you type.


^\


> `\` for anonymous functions

A one-character ASCII rendering of the Greek lowercase letter lambda: λ

λx → x + 5

\x -> x + 5


Opposite effect one, lost interest in Roc after reading that.

If anything I don't think Haskell goes far enough the automatic currying, points free stuff. If you're going to be declarative, don't half ass it.


We recently changed Roc's lambda syntax from the syntax that languages like Elm and Haskell use...

    foo = \arg1, arg2 ->
        body
...to this:

    foo = |arg1, arg2|
        body
The reason for this change was that we have a new and extremely well-received language feature (landed but not yet formally announced) which results in `->` and `=>` having different meanings in the type system. This made it confusing to have `->` in the syntax for anonymous functions, because it seemed to suggest a connection with the type-level `->` that wasn't actually there.

The most popular syntax that mainstream languages use today for anonymous functions is something like `(arg1, arg2) => body` but of course that has the same problem with having an arrow in it, so changing to that wouldn't have solved the problem.

Rust uses `|arg1, arg2| body` (and Ruby kinda uses it too for blocks), and we'd all had fine experiences using that syntax in Rust, so we chose it as the new lambda syntax. You can see the new syntax in the code example at the top of roc-lang.org.


R has that too.


really? thought it just function(x)


Yeah Rust compiles slowly, so we need two more half-baked languages - Zig and Roc, both of which I couldn't care less.

Rust's slow compilation comes from lots of features and an excellent generated machine code quality. Both Zig and Roc will be equally slow or slower if they match what Rust offers.

If all they want is fast compilation, they can just try Pascal.


> \ for anonymous functions

this didn't faze me in the least because it's just a more easily typed λ, the lambda character, which has for a long time now (many decades) been used to describe anonymous functions (i.e. "lambdas")

did you never take any formal CS education? if not, that might explain it

so before you jump to calling it "dumb", maybe next time lean on Chesterton's Fence for a bit.

https://sproutsschools.com/chesterton-fence-dont-destroy-wha...

That said, granted, the fact that it's also the escape character is problematic. Maybe /\ might have been better but that's even harder to type.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: