Most active commenters
  • int_19h(4)
  • fsloth(3)

←back to thread

611 points LorenDB | 36 comments | | HN request time: 1.426s | source | bottom
1. choeger ◴[] No.43912866[source]
All this has been known in the PL design community for decades if not half a century by now.

Two things are incredibly frustrating when it comes to safety in software engineering:

1. The arrogance that "practitioners" have against "theorists" (everyone with a PhD in programming languages)

2. The slowness of the adoption of well-tested and thoroughly researched language concepts (think of Haskell type classes, aka, Rust traits)

I like that Rust can pick good concepts and design coherent language from them without inventing its own "pragmatic" solution that breaks horribly in some use cases that some "practitioners" deem "too theoretical."

replies(4): >>43913128 #>>43915349 #>>43916215 #>>43917609 #
2. Ygg2 ◴[] No.43913128[source]
> I like that Rust can pick good concepts and design coherent language from them without inventing its own "pragmatic" solution that breaks horribly in some use cases that some "practitioners" deem "too theoretical."

I've thought Rust picked some pretty nifty middle ground. On one side, it's not mindfucking unsafe like C. It picked to remove a set of problems like memory safety. On the other side, Rust didn't go for the highest of theoretical grounds. It's not guaranteeing much outside of it, and it also relies a bit on human help (unsafe blocks).

replies(1): >>43913258 #
3. eptcyka ◴[] No.43913258[source]
As per the article, Rust has benefits beyond the ones afforded by the borrow checker.
replies(1): >>43913710 #
4. Ygg2 ◴[] No.43913710{3}[source]
Sure, but it is pragmatic in other ways as well :)

It takes ADT, but not function currying, and so on.

replies(2): >>43913760 #>>43913876 #
5. jeffparsons ◴[] No.43913760{4}[source]
I've occasionally wondered about the lack of currying in Rust; it feels like something that can be done mechanically at compile time, so why not support it? Perhaps to do it cleanly (without more magical privileged functions in core) would require variadic generics?
replies(3): >>43917073 #>>43918247 #>>43922424 #
6. andrepd ◴[] No.43913876{4}[source]
I don't think currying is that big a deal, it's just syntactic sugar that might or might not make things easier to read, unlike ADTs or closures which are important core concepts.

I'd love to have a syntax like

    { foo(%1, bar) }
standing for

    |x| { foo(x, bar) }
though. I'm not aware of any language that has this!
replies(10): >>43914073 #>>43914288 #>>43914655 #>>43914762 #>>43914803 #>>43915598 #>>43916123 #>>43920249 #>>43922459 #>>43925563 #
7. Munksgaard ◴[] No.43914073{5}[source]
Elixir has this, which is close:

    &foo(&1, bar)
8. tcfhgj ◴[] No.43914288{5}[source]
Powershell has this, why do you like this?
9. masijo ◴[] No.43914655{5}[source]
Clojure has this

    user=> (#(println %1 %2) "Hello " "Clojure")
    Hello Clojure
10. tormeh ◴[] No.43914762{5}[source]
I'd argue that currying is actively harmful (I suspect you agree). I've seen functions take one argument in one source file, and the next argument in another source file. In JavaScript no less. Horrendous stuff. One of my most hated anti-features, along with Scala's implicits. These kinds of features are mostly misused rather than used.
replies(1): >>43917127 #
11. tmoertel ◴[] No.43914803{5}[source]
Mathematica has this:

    In[1]:  Map((1 + #1)&, {a, b, c})
    Out[1]: {a + 1, b + 1, c + 1}
See https://reference.wolfram.com/language/ref/Function.html.en for the full story.
replies(1): >>43922222 #
12. sanderjd ◴[] No.43915349[source]
Yep, this article is a good example of one way that c++ is bad, but it's not really a great example of rust being particularly good; many other languages support this well. I'm very glad Rust is one of those languages though!
replies(1): >>43917770 #
13. GrantMoyer ◴[] No.43915598{5}[source]
C++ has this[1], kind of, but please don't actually use it.

> for_each(a.begin(), a.end(), std::cout << _1 << ' ');

Also, Scala has something similar as a first class feature[2].

[1]: https://www.boost.org/doc/libs/1_88_0/doc/html/lambda.html

[2]: https://scala-lang.org/files/archive/spec/3.4/06-expressions...

14. icen ◴[] No.43916123{5}[source]
In K the arguments are named x y z by default, so you just write:

    { foo[x, bar] }
15. blub ◴[] No.43916215[source]
If the practitioners haven’t adopted what you’re offering for 50+ years, that thing can’t be good.

Rust is also struggling with its “too theoretical” concepts by the way. The attempts of the community to gaslight the practitioners that the concepts are in fact easy to learn and straightforward are only enjoying mild success, if I may call it that.

replies(3): >>43916562 #>>43918668 #>>43920318 #
16. db48x ◴[] No.43916562[source]
I disagree. The advertising and hype pushing people to use C++ is insane. There are hundreds of magazines that exist solely to absorb the advertising budget of Microsoft (and to a lesser extent Intel). Hundreds of conferences every year. You could be writing code in ML at your startup with no complaints and demonstrable success but as soon as your company gets big enough to send your CEO to an industry conference you’ll be switching to C++. The in–flight magazine will extol the benefits of MSVC, speakers like Matt Godbolt will preach Correct by Construction in C++, etc, etc. By the time he gets back he’s been brainwashed into thinking that C++ is the next best thing.
17. bunderbunder ◴[] No.43917073{5}[source]
You can absolutely curry functions in Rust. You just have to do it manually because there's no syntactic sugar for it.

I think that's a good thing. A curried function is a function that takes one argument, and returns a closure that captures the argument. That closure might itself take another argument, and return a new closure that captures both the original argument and the second one. And so on ad infinitum. How ownership and borrowing works across that chain of closures could easily become a touchy issue, so you probably want to be making it as explicit as possible.

Or perhaps better yet, find an easier way to accomplish the same task. Maybe use a struct to explicitly carry the arguments along until you're ready to call the function.

18. bunderbunder ◴[] No.43917127{6}[source]
From what I've seen, partial application tends to be used for utmost good in dialects of ML, and utmost evil most everywhere else. Chaotic neutral in R/tidyverse.
19. bigbuppo ◴[] No.43917609[source]
It's weird that this sort of debate around C++ often leaves out the fact that many of the problems with C++ were known before C++ even existed. Outside of a few specific buckets, there is no reason to use C++ for any new projects, and really, there never has been. If you can't stomach Rust for some reason, and I'm one of those people, there are plenty of choices out there without all the pitfalls of C++ or C.
replies(1): >>43918203 #
20. groos ◴[] No.43917770[source]
I had the same thought - what Matt's examples required was strong typing and that has existed for very long time outside of the C family world.
21. ivmaykov ◴[] No.43918203[source]
> If you can't stomach Rust for some reason, and I'm one of those people, there are plenty of choices out there without all the pitfalls of C++ or C.

Unless you are doing embedded programming ...

replies(1): >>43918549 #
22. skybrian ◴[] No.43918247{5}[source]
This isn’t Rust-specific, but one reason a language designer might deliberately not implement currying is that it makes function call sites a bit harder to read. You need to already know how many arguments a function takes to make sense of the call’s return type.
23. fsloth ◴[] No.43918549{3}[source]
I think embedded is one of the specific buckets.

You target the compiler your client uses for their platform. There is very little choice there.

replies(1): >>43922404 #
24. fsloth ◴[] No.43918668[source]
”If the practitioners haven’t adopted what you’re offering for 50+ years, that thing can’t be good.”

I don’t think what features are popular in C++ is good indication of anything. The language is good only due to the insane amounts of investment to the ecosystem, not because of the language features due to design.

For an industrial language inventory of ”nice features to have” F# and C# are mostly my personal gold standard.

”Too theoretical” is IMO not the correct lens to use. I would propose as a better lens a) which patterns you often use b) how to implement them in language design itself.

A case in point is the gang-of-four book. It mostly gives names to things in C++ that are language features in better languages.

replies(1): >>43920356 #
25. cobbal ◴[] No.43920249{5}[source]
swift has this as well

    { foo($0, bar) }
26. ordu ◴[] No.43920318[source]
> If the practitioners haven’t adopted what you’re offering for 50+ years, that thing can’t be good.

I wouldn't trust opinion of practitioners. Not after they have chosen javascript and, God forbid, PHP. Practitioners choose not what is inherently good, but what is popular. It is a very practical choice that brings a lot of benefits, so it is just practitioners being practitioners. It can be good or bad, I don't care now, it doesn't matter for my argument. The issue that a good thing can be overlooked by practitioners for decades, because there is nothing popular giving them this thing.

replies(1): >>43920601 #
27. senderista ◴[] No.43920356{3}[source]
The GoF book uses Smalltalk for the examples as well as C++, and Smalltalk is about the most expressive imperative language you could ask for.
28. j_w ◴[] No.43920601{3}[source]
But being popular can be very important for practitioners. How else are you going to hire other practitioners?
29. zelphirkalt ◴[] No.43922222{6}[source]
What is that & doing there? I thought it looks neat, but then that & ...
replies(1): >>43941722 #
30. int_19h ◴[] No.43922404{4}[source]
Even then you're probably better off using something safer that transpiles to C.
replies(1): >>43925522 #
31. int_19h ◴[] No.43922424{5}[source]
Currying is just syntactic sugar for a lambda that binds the first argument and passes the rest unchanged, and you can achieve the same level of conciseness in other ways - e.g. "pipeline" operators - but with a syntax that is more readable and more extensible. E.g. https://docs.rs/piping/latest/piping/
32. int_19h ◴[] No.43922459{5}[source]
As noted in other comments, a bunch of languages has it, but the problem with this syntax is that it doesn't generalize well. For example, let's say we have:

   foo(%1, bar(%1, 42)) 
does this desugar to:

   |x| foo(x, |y| bar(y, 42)) 
or to:

   |x| foo(x, bar(x, 42)) 
and do you trust the person reading this code later to remember which one it is?
33. fsloth ◴[] No.43925522{5}[source]
”Transpiles to c” - how do you generally optimize single line performance hotspots in that case?
replies(1): >>43930952 #
34. _flux ◴[] No.43925563{5}[source]
I think currying is an important concept and not sugar at all, as it means each function takes exactly one parameter, i.e. in OCaml foo bar baz is equal to (foo bar) baz. Of course, some other languages can try to emulate the use of currying and the original original idea no longer exists there.

I'm relatively sure there exists such a language you are looking for, but I've forgotten its name :(.

There was a syntax extension pa_holes for OCaml (which you perhaps know, as it is natively currying) that worked like

    (\ foo \1 bar)
and it would become

    fun x -> foo x bar
I'm sure the extension was inspired by some other language.. And the pa syntax extension extension mechanism for OCaml has been replaced by ppx a long time ago already; maybe the new system doesn't enable such succinct extensions.
35. int_19h ◴[] No.43930952{6}[source]
I don't see why that would make any difference? The generated C code is just a build artifact in this case, similar to IRs often used by compilers internally. You don't think about e.g. gcc IR when you optimize hotspots in code written in C, though - you just look at the C source and the generated asm code. If you have, say, Zig transpiling to C (which it can do with `-ofmt=c`), you'd similarly look at the Zig source and the generated asm code.
36. tmoertel ◴[] No.43941722{7}[source]
The & is a postfix operator that converts an expression into an anonymous function. See “Pure Functions”:

https://reference.wolfram.com/language/tutorial/FunctionalOp...