Live data from Hacker News

Microfeatures I'd like to see in more languages

buttondown.email

391–400 of 539 posts

Re: Microfeatures I'd like to see in more languages

#391
post #4

My favorite is uniform function call syntax. In several languages (Nim, Koka, D, …), you can always write bar.foo(baz) instead of foo(bar, baz) and vice-versa. Another one from Nim is the implicit result variable. Instead of having to do this: func sum(nums: seq[int]): int = var result = 0 for num in nums: result += num return result you just do this: func sum(nums: seq[int]): int = for num in nums: result += num It…

I've only seen this proposed as a feature to help with template meta-programming (though it could also make similar sense in any dynamic language).

There, it helps if a template can say `t.foo()`, and, with UFCS, can use that template with any t for which either `foo(t)` or `t.foo()` exist. In contrast, in C++ today, a template using `t.foo()` limits its own use only to types that have a foo() method, probably unnecessarily (note that writing `foo(t)` in a C++ template is less limiting, as someone who controls neither the template nor the type of t can still define that function).

However, outside of this use case, I think conflating these two is more of a negative than a positive. It means that there are twice as many places where I may need to lookup the definition of foo(), at the very least. So I wouldn't add this to any static language that doesn't support templates or macros.

Re: Microfeatures I'd like to see in more languages

#393

Earlier quoted context omitted.

> My favorite is uniform function call syntax. In several languages (Nim, Koka, D, …), you can always write bar.foo(baz) instead of foo(bar, baz) and vice-versa. To me, these are "Tell bar's foo to do something with baz." and "Tell foo to do something with bar and baz.". So being 'able' to flipflop the syntax is at least temporarily semantic'ly confusing.

Except I find the concept of state "doing something with" other state unhelpful. Why is bar working with baz and not baz working with bar? I struggle with this in Unity: Player.collect(PickUp) // this? PickUp.boost(Player) // ...or this? Instead, the code should describe the interaction between the two units of state: onCollide(Player, Pickup) If we structure our code like in the last example, it makes sense to weake…

It shouldn't be that confusing. Ideally, a method should be defined on an object only if it needs to access the encapsulated state of that object. Otherwise, it should probably be a free function.

So, in your case, depending on other modeling decisions, I could argue either for

onCollide(Player, Pickup) - if Player and Pickup are both plain data, and don't need to guarantee any invariants

Player.collect(Pickup) - if Player actually has to ensure some invariants such as healthI don't see any good arguments for pickup.boost(Player), in typical games. Of course, if both the Pickup and the Player have some invariants that need to be maintained on a collision, then arguably the design has to be changed at a deeper level.

Re: Microfeatures I'd like to see in more languages

#394
post #42
post #4

My favorite is uniform function call syntax. In several languages (Nim, Koka, D, …), you can always write bar.foo(baz) instead of foo(bar, baz) and vice-versa. Another one from Nim is the implicit result variable. Instead of having to do this: func sum(nums: seq[int]): int = var result = 0 for num in nums: result += num return result you just do this: func sum(nums: seq[int]): int = for num in nums: result += num It…

and vice-versa Doesn't the reverse pollute the function namespace? If every obj.fun() can be written as fun(obj), doesn't that cause ambiguity with a previously imported global function fun()?

In a language without function overloading or namespaces/modules, yes, it would be a major limitation.

But since most languages have both, I don't think it's a serious concern. I don't know of any language where the only names pace support is classes, so that all functions go in a global namespace unless they are methods on a class - maybe you could argue C works like this (where "methods" are function pointer member of a struct)?

Re: Microfeatures I'd like to see in more languages

#395
post #9

Earlier quoted context omitted.

I think you can also do the second in Go with named returns, e.g. func sum(nums []int) (result int) { for _, n := range nums { result += n } } No clue if it's an idiomatic usage, and named returns always felt a little too magic for me.

Lots of languages will implicitly return the final expression— I feel like that's a decent compromise. Not quite as magical as an actual named variable that just exists, but not as clunky as needing as explicit `return` every time.

Honestly, I don't like that style for languages which allow multiple side-effects as well. For example,

  (progn 
    (print "something") 
    0)
Is honestly pretty ugly from my point of view.

Re: Microfeatures I'd like to see in more languages

#396
post #379

Earlier quoted context omitted.

Oh I didn’t know it was kind of a workaround. I do like the fact that loop is not a function though but an expression like if or case. FWIW I think in Clojure you can use “recur” inside functions too to specifically indicate tail call recursion without relying on automatic optimization

> without relying on automatic optimization I didn't think Clojure had any automatic optimization at all, due to the JVM not supporting it.

I think you mean automatic tail call optimization?

(JVM has quite a lot of automatic optimizations that Clojure enjoys automatically, and clojure itself also has some automatic optimizations).

Re: Microfeatures I'd like to see in more languages

#397

Earlier quoted context omitted.

And that's why all modern languages implement streams/string helpers/string builders. You do not want to actually write strings/manipulate them using "+" (concatenation symbol) in code directly because, in modern Unicode world, it tends to become a point of failure for obscure bugs / a maintenance horror show.

String builders originated in languages with immutable strings making code using something like "foo += bar" in a loop very expensive due to the need to allocate a new string on every iteration. A string builder is basically a mutable string that can be built in-place efficiently and converted to a proper immutable string at the end. It is purely a performance thing, and there are no Unicode issues when concatenating…

Note that some kind of string builders are necessary for efficient repeated concatenation both in languages with immutable strings and in languages with 0-terminated strings.

Using strcat() repeatedly in C for example will mean that the string is being read over and over again to find the end, making an O(n) loop actually O(n²).

Re: Microfeatures I'd like to see in more languages

#398

Keyword and optional arguments (seen in e.g. Python, Bash with --flags, and OCaml) are my favorite language superpower. They make code more self-documenting and let you add add optional behaviors to functions. This makes it really easy to make concise, highly usable APIs.

I love Common Lisp arguments support, where you can also get a parameter that tells you whether an optional or keyword argument was supplied by the caller, instead of relying on the default value, for when you want to know. It's very useful for something like a patch function.

Re: Microfeatures I'd like to see in more languages

#399
post #389

Earlier quoted context omitted.

My guess is that they did that because there are real human languages where ' is used as a thousands separator.

There are also real human languages where a space is used as a thousands separator, and an underscore is kind of the programming equivalent of a space.

A half space is British Standard for thousands separator if memory serves, also standard in Norway.

Re: Microfeatures I'd like to see in more languages

#400
post #4

My favorite is uniform function call syntax. In several languages (Nim, Koka, D, …), you can always write bar.foo(baz) instead of foo(bar, baz) and vice-versa. Another one from Nim is the implicit result variable. Instead of having to do this: func sum(nums: seq[int]): int = var result = 0 for num in nums: result += num return result you just do this: func sum(nums: seq[int]): int = for num in nums: result += num It…

> My favorite is uniform function call syntax. In several languages (Nim, Koka, D, …), you can always write bar.foo(baz) instead of foo(bar, baz) and vice-versa. To me, these are "Tell bar's foo to do something with baz." and "Tell foo to do something with bar and baz.". So being 'able' to flipflop the syntax is at least temporarily semantic'ly confusing.

The main selling point is the ability to chain calls

  foo.do_this(bar).do_that(baz)
instead of

  do_that(do_this(foo, bar), baz)
Post reply on HN