7 ms·
Ask HN: Great programming language features, other languages should steal?
You cannot be fluent in all programming languages. But this also makes you blind for language features a particular language really got right, or killer libraries that are just amazing. Which are these widely unknown features, that you only get to know, when working with a language and its ecosystem?
Example: I'm coming from Scala. Starting to develop a Flutter app with Dart, made me realize how great and safe the serialization ecosystem in Scala is. You can serialize types (and type hierarchies) with a convenient syntax that is completely decoupled from the type itself, while high-performance serialization code is generated at compile-time via macros. Libraries include: https://circe.github.io/circe/ https://github.com/suzaku-io/boopickle
- mrkeen 6y ago* Pattern matching with totality checking. Warn us if a cased is missed. * Higher-kinded types. Can a List<Int> and a Stream<Int> both be considered a T<Int> ? * Differentiate between functions with and without effects.
- warmind99 6y agoI really like the trait/struct/enum model of programming that you find in Rust. I'm a quant and I do a lot of higher math, and the trait method of programming is very very similar to how you might construct algebraic structure in the set=>group=>field etc way.
- MosheZada 6y agoJavaScript’s object destructuring
- ktm5j 6y agoI'm a big fan of scope guards in D: https://tour.dlang.org/tour/en/gems/scope-guards https://tour.dlang.org/tour/en/gems/scope-guards
- joshsyn 6y agoMy ideal language is something like F# but with HKTs, trait, macros, union types (without discriminator), basically most features dotty aka scala 3 introduced.
- joshsyn 6y agoIts funny though, as I am personally switching to scala 3 for the features I mentioned. I find the features particularly handy for building distributed systems using Akka. And I wished they'd get tooling just right out of the bat. (sbt doesn't seem great)
- ulam2 6y ago1. Python's list slicing and indexing is the perfect mix of simplicity, intuition and power. It is surprising how few languages have similar implementations. 2. Currying like in Haskell.
- jzoch 6y ago1. Pattern matching with destructuring a la scala, rust, ocaml, etc. 2. scoped threading a la kotlin and by using certain libraries, rust to a degree. 3. structs / records (i feel bad putting this since every language but java seems to have it). 4. macros of some kind 5. type inference 6. enums a la rust / sealed types in kotlin which are needed for pattern matching afaik
- bjoli 6y agoR6rs scheme's (or even better Racket's) macro system. Recently similar systems have been making their way into other languages, but the AST-like syntax of lisps make it a fair bit easier to use. Other than that? Delimited continuations. Guile-fibers, a parallel concurrent ML (which I would describe as a generalisation of go's concurrency model) for guile scheme is implemented using them as a scheme-only library. No low level voodoo. Just straight forward scheme.
- non-entity 6y agoI like how Erlang handles binary and bitstring data. Extremely useful if you're doing any sort of protocol implementation.
- Jtsummers 6y agoSo many programs I've written would have been easier with Erlang's binaries and pattern matching. A lot of what I've done has boiled down to parsing and transforming binaries from one format to another. I've made a few POCs for work in the past (that I couldn't have convinced them to use no matter what, only C/C++/C#/Python were used) that demonstrated a much simpler version of core program logic because of those two features. The code was often 1/10th the size (if not smaller), clearer, and more extendable. And this wasn't just because I was writing it for a second (or third) time. The code in the other languages was good, clean code as well. It's just harder in those languages to express the same things as concisely.
- didip 6y ago1. I like Erlang's Supervisor. If Go's goroutine has a unique ID, I think it can steal that design. 2. There was one new language that I don't remember the name, its data structures automatically turned to thread-safe upon concurrent access. That's smart. I wish all languages do that. Less data structure names people have to remember. 3. The compiler can cross-compiles to many different architectures similar to Go or Zig. This makes deployment story much simpler. 4. The compiler produces single binary by default. This also makes deployment story simpler. 5. This is year 2020. Every language must have an event loop so that they can have async I/O routines. Preferably in the form of CSP (green threads + channels). As you can see I care about the ergonomics and tooling outside the language itself.
- dfalzone 6y agoThis is pretty minor but I like how in Rust you can use constructs like if-statements and match-statements as expressions. For example: let foo = if some_condition { "condition is true" } else { "condition is false" }; let bar = match x { 0 => "string", 1 => "another string", _ => "default" }; It's not super significant but I think it's an elegant syntactic feature.
- Foober223 6y agoThey got that from lisp. But in lisp it's more than just a syntax trick that only works for a few predefined cases.
- Jtsummers 6y agoAnd Algol and others. It's a nice feature when available, certainly my preference. I find I can often write things more cleanly given an expression oriented languages than ones dominated by statements.
- deleted 6y ago[deleted]
- kazinator 6y agoYou could do this in GNU C probably at least as far back as 1990. In GNU C, this syntax: ({ expr1; expr2; ... ; exprN }) gives you something like a Lisp progn: it's an expression, whose value is exprN. Complete sample: #include <stdio.h> int main(int argc, char **argv) { char *foo = (argc > 1) ? ({ printf("consequent\n"); "condition is true: I have arguments"; }) : ({ printf("alternative\n"); "condition is false: I don't have arguments"; }); puts(foo); } This is almost certainly because RMS is a Lisp hacker. I.e. why GCC uses Lisp notations internally and functions with "mapcar" in the name and such. I discovered it by accident sometime in 1993. I wanted the value of the last expression in a braced block. Maybe it can forced into an expression if you just put parentheses on it? By golly, the intuition worked. Mainly, it is used in writing macros. Speaking of which: #include <stdio.h> #define ifblock(expr) (expr) ? ( #define elseblock ) : ( #define endblock ) int main(int argc, char **argv) { char *foo = ifblock (argc > 1) { printf("consequent\n"); "condition is true: I have arguments"; } elseblock { printf("alternative\n"); "condition is false: I don't have arguments"; } endblock; puts(foo); }
- verdverm 6y agoGolang, avoid this idea in the service of software engineering. Go tries to be a syntax minimalist so that the writers can make what's there the best rather than building the world of fancy new PL features. It also means there are fewer days of doing the same thing and most people's code looks the same. <- this is such a difference maker
- deepaksurti 6y agoCommon Lisp condition system [1] https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node317.html https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node317.html [2] http://gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html http://gigamonkeys.com/book/beyond-exception-handling-condit...
- stakkur 6y agoPython data structure manipulation/comprehensions. I realize there's some overlap with other languages, but this to me is Python's 'killer' feature.
- raja_mukherji 6y agoGoal-directed evaluation from Icon. Makes generators composable.
- dTal 6y agoIt's more of a paradigm than a feature but, multiple dispatch à la Julia. One of the creators of Julia makes a strong case that it is the key to libraries that "compose" without being aware of one another: https://www.youtube.com/watch?v=kc9HwsxE1OY https://www.youtube.com/watch?v=kc9HwsxE1OY
- Foober223 6y agoThey got that from lisp.
- bjoli 6y agoJulia compensates for time lost by making a lot more use of it in the stdlib than, say, most CL implementations do. It is as if the julia people saw it and went "wow, this is good!" and had it in from the beginning. With their focus on maths, it is hardly surprising.
- eigenspace 6y agoThere's actually a pretty fundamental reason that Julia's Base and stdlib makes more use of multiple dispatch than Common Lisp (or any other language in existence with multiple dispatch). It's that in basically every other language, multiple dispatch comes with a non-neglible performance penalty. Julia's multiple dispatch semantics were designed around having a JIT compiler, and it's JIT compiler design was designed around having multiple dispatch, and this tight integration let us be really good at de-virtualizing the dispatch, removing the hefty performance pentalty. This is why even basic arithmetic operations like +, * and whatnot are able to be generic functions (with a gigantic number of methods, 184 for + and 364 for * currently just in Base and LinearAlgebra!) without any runtime performance compromise. By contrast, Nim for instance just removed multiple dispatch in their 1.0 release because nobody was using it due to being so slow.
- bjoli 6y agoThanks! I always suspected you could have a fast dynamic dispatch in a typed language if you locked it down enough to not allow redefinitions at runtime and such (which removes a lot of the fun of CLOS). It makes sense to let the jit do the heavy lifting though!
- xinau 6y agoGolang's level of boringness. It's just boring enough to still be useful. Often it's easy to understand random code and write "reasonable" code.
- kmatthewc 6y agoNullability built into the type system like Kotlin. one main difference between kotlin optionals and swift options (for example) is that Kotlin nullability is enforced during compile time. I'm not aware of many languages which do that other than Kotlin.
- deleted 6y ago[deleted]
- thehappypm 6y agoEvery language should have robust, standard collections functions like Python does. Once you start doing quick things with lists like the zip function, or list comprehensions, it's hard to go back.
- p0llard 6y ago> Once you start doing quick things with lists like the zip function, or list comprehensions, it's hard to go back. Totally agree with you on this one, but I'm not sure I'd go so far as to say that they're a defining feature of Python, more of functional languages, from which Python has borrowed a thing or two.
- thehappypm 6y agoAgreed! Python is just one example of a language that does it well.
- tdeck 6y agoPersonally I think Ruby does a much nicer job of this, although I use both languages on a regular basis. A complx chain of map and filter calls tends to be a lot more readable than a complex nested list comprehension.
- ghj 6y agoPython collections doesn't have basic data structures like red black trees (which are in java as java.util.TreeMap or c++ as std::map). You end up needing third party libraries like sortedcontainers.
- manu3000 6y agoalgebraic data types and parametric polymorphism, so you can define an option type for computations that fails and get rid of nulls
- bjourne 6y agoFactor allows almost any special character in word (function) names. It is incredibly useful when you get the hang of it. I would love to have it in other languages. E.g. is_prime(1234) => prime?(1234), append_inplace(l, e) => append!(l, e), array_equal(a, b) => array=(a, b), midi_to_mp3(a) => midi>mp3(a), reciprocal(a) => ^-1(a) Lisp can do some of it but not "()" or (I think) "`';"
- kazinator 6y agoIn Common Lisp you can use pretty much any character in a symbol name, if you escape it: [3]> '|(``"| |(``"| [4]> (defvar |(``"| 42) |(``"| [5]> |(``"| 42 It's pretty dubious for code; it's useful when working with some data format that has identifiers with its own validity rules. There is more than one way to escape: vertical bars or backslashes: [6]> (eq '|(``"| '\(\`\`\") T
- dbsmith83 6y agoKotlin Extension Functions https://kotlinlang.org/docs/reference/extensions.html https://kotlinlang.org/docs/reference/extensions.html I personally love it for when I have different data classes for different layers of the application (REST objects vs. controller level objects vs. Database objects) and need to convert between them. I simply just add an extension to one layer's data object to convert it to the other layer's type