Live data from Hacker News

Show HN: A small programming language where everything is pass-by-value

github.com

51–60 of 64 posts

Re: Show HN: A small programming language where everything is pass-by-value

#51

But what if mutation is intended? How to pass a mutable reference into a function, so that it can change the underlying value and the caller can observe these changes? What about concurrent mutable containers?

> How to pass a mutable reference into a function, so that it can change the underlying value and the caller can observe these changes?

Just modify the value inside the function and return it, then assign back. This is what the |= syntax is designed for. It's a bit more verbose than passing mutable references to functions but it's actually functionally equivalent.

Herd has some optimisations so that in many cases this won't even require any copies.

> What about concurrent mutable containers?

I've considered adding these, but right now they don't exist in Herd.

Re: Show HN: A small programming language where everything is pass-by-value

#53
post #26

Earlier quoted context omitted.

Why don’t we just do this by default for threading in most languages? It’s pretty rare for me to actually want to do memory sharing while threading (mostly because of the complexity)

Because it's super slow and shared memory is super fast. And people generally prefer fast code rather than safe code.

It's not "super slow" and most languages do something very similar within concurrent data structures.

Also, copy by value in itself is just a semantic requirement, it doesn't say how it's implemented.

And shared mutable memory is pretty damn slow (given you are not fine with data race garbage), because atomic operations destroy caches. So it's the usual space-time tradeoff at the end of the day.

Re: Show HN: A small programming language where everything is pass-by-value

#55

The threading story here is what grabbed my attention. Pass-by-value with copy-on-write means you get data-race immunity without any locks or channels. You just pass data to a thread and mutations stay local. That's a genuinely useful property. I've worked on systems where we spent more time reasoning about shared state than writing actual logic. The typical answer is "just make everything immutable" but then you los…

Hmm this is a bit like peeling a banana only to throw the banana and eat the peel. Pass by value reduces the true benefit of copy-on-write. Use immutable pass by reference. Make a copy only if mutability is requested in the thread. This makes concurrent reads lock-free but also cuts down on memory allocations.

Peels are rich in fiber.

Re: Show HN: A small programming language where everything is pass-by-value

#56
My hobby language[1] also has no reference semantics, very similar to Herd. I think this is a really interesting point in the design space. A lot of complexity goes away when it's only values, and there are real languages like classic APL that work this way. But there are some serious downsides.

In practice I have found that it's very painful to thread state through your program. I ended up offering global variables, which provide something similar to but worse than generalized reference semantics. My language aims for simplicity so I think this may still be a good tradeoff, but it's tricky to imagine this working well in a larger user codebase.

I like that having only value semantics allows us, internally, to use reference counted immutable objects to cut down on copying; we both pass-by-reference internally and present it as pass-by-value to the programmer. No cycle detection needed because it's not possible to construct cycles. I use an immutable data structures library[2] so that modifications are reasonably efficient. I recommend trying that in Herd; it's almost always better than copy-on-write. Think about the Big-O of modifying a single element in an array, or building up a list by repeatedly appending to it. With pure COW it's hard to have a large array at all--it takes too long to do anything with it!

For the programmer, missing reference semantics can be a negative. Sometimes people want circular linked lists, or to implement custom data structures. It's tough to build new data structures in a language without reference semantics. For the most part, the programmer has to simulate them with arrays. This works for APL because it's an array language, but my BASIC has less of an excuse.

I was able to avoid nearly all reference counting overhead by being single threaded only. My reference counts aren't atomic so I don't pay anything but the inc/dec. For a simple language like TMBASIC this was sensible, but in a language with multithreading that has to pay for atomic refcounts, it's a tough performance pill to swallow. You may want to consider a tracing GC for Herd.

[1] https://tmbasic.com

[2] https://github.com/arximboldi/immer

Re: Show HN: A small programming language where everything is pass-by-value

#57
post #37

Earlier quoted context omitted.

> the references are just an internal optimization Optimization specifically for function calls, or... ? Because if you're doing all this copy-on-write anyway, the indirection seems to be a needless cost in other contexts.

This applies everywhere, and it fundamentally wouldn't be possible for just function calls. > needless cost Are you comparing to a language with mutable references or a our functional language? A language with mutable references will of course be faster, but this is more intended as an alternative to pure functional languages (since functions are referentially transparent). In this case, the cost of the indirection i…

I'm comparing to the same language implemented without the supposed internal optimization, according to my understanding of what you're doing.

But I think at this point I'd have to read and analyze the code to have a proper understanding.

... Although granting that you already have paid the cost of reference-counting GC (and, I assume, per-object allocation), it probably is indeed insignificant. And special-casing where the reference count == 1 is also kinda neat. (E.g. CPython doesn't "move references" per se if I'm thinking clearly; but it does detect cases where it can safely mutate the underlying implementation of a type that's "immutable" from the perspective of language syntax.)

Re: Show HN: A small programming language where everything is pass-by-value

#58

Nobody has heard of persistent data structures?!

This is similar but not quite the same as persistent data structures. In particular:

- We can avoid quite a few allocations in loops by mutating lists/dicts in place if we hold an exclusive reference (and after the first mutation, we always will). Updates to persistent data structures are relatively cheap, but they're a lot more expensive than an in-place update.

- Herd has syntax sugar for directly modifying nested values inside lists/dicts. E.g. `set foo.bar.[0].baz = 1;`.

In practice, is this faster than a different implementation of the same semantics using persistent data structures and a tracing GC? That will depend on your program.

Re: Show HN: A small programming language where everything is pass-by-value

#59

Syntax comment: in your control structures you use a keyword ("do", "then") to start a block as well as wrapping the block in parentheses. This feels superfluous. I suggest sticking with either keywords or parens to delineate blocks, not both.

This is a little bit tricky because the parser has to distinguish between:

  for x in arr (something ())
           \                 /-- function call
and

  for x in arr (something ())
               \            /-- loop body

This is consequence of combining "blocks" and "precedence" into the same construct ().

A more fitting example would be to support:

  for x in arr do set z += x;
  for x in arr do something x;
IIRC these both currently require an explicit block in my parser.

Re: Show HN: A small programming language where everything is pass-by-value

#60
post #8
post #5

> In herd, everything is immutable unless declared with var So basucally everything is var?

I'm not sure if I understand the question? There are two ways to define a variable binding: x = 1; // declares x as immutable var y = 2; // declares y as mutable The "default" behaviour (if no keyword is used) is to define a new immutable variable.

I'm asking if the reality wouldn't be that "everything" is set as mutable by default and the non mutable part is ignored.
Post reply on HN