Live data from Hacker News

A few good ideas in programming languages

prydt.xyz

31–40 of 64 posts

Re: A few good ideas in programming languages

#31

> Borrow Checking It's very confusing name for this feature. It suggest that some sort of borrowing takes place and that it's just an optional check, which isn't the case. It should be named something like "enforced static usage analysis" instead. In my programming language I have similar mechanism. But it isn't just checking, since it affects code generation by tracking which variables are still in use and which can…

You can very likely borrow check in languages that don't have it in the type system. Exactly the way you suggest, as an optional add-in. It's still WIP but in my side project I haven't found cases that can't be handled yet. https://github.com/ityonemo/clr

> Exactly the way you suggest, as an optional add-in

No, I don't suggest it, but criticize it. Rust performs its checking as a separate step after actual compilation, which sometimes leads to strange behavior (like borrow errors are shown only after actual compilation errors). I prefer an approach which is integrated with other language mechanisms.

> It's still WIP but in my side project I haven't found cases that can't be handled yet.

It's generally a good idea to write such an analyzer, but I doubt it can be useful without proper integration with the language itself (with huge semantics changes). If it's too strict, it will reject perfectly fine code, but otherwise it will catch only the most obvious errors and approve code having more complex memory bugs.

Re: A few good ideas in programming languages

#32
post #29

A genuine question: is the first point (flow typing / type narrowing) a subset of or intersection with or just an alias to SSA (static single assignment)? I'm playing with a small interpreted language implementation that is based on Lua, and have reached a point where I want to implement a single-pass SSA (there is a nice short CS paper on this), but cannot get my head around all the concepts, even if I need proper S…

No. Type systems are unrelated to abstract machines which are unrelated to usability. Type inference/checking happens early in the pipeline. SSA is a way of laying out assembly instructions for an abstract machine. I say abstract because real machines re-assign values to the same addresses over time (which is precisely what 'single' static assignment prescribes against). Once you know which registers your real machin…

Ok. More thoughts.

I was trying to see what was special about Crystal in this regard.

It seems like if you took any ML or Haskell-like, you'd have type inference.

Then you could allow shadowing (Rust-style) meaning the same symbol in the source code would be one variable now, and a different variable later.

Then your compiler would need to distinguish x into x1 and x2 so it could track them separately.

So yeah, kind of an SSA I guess!

Re: A few good ideas in programming languages

#33
post #23

> Borrow Checking It's very confusing name for this feature. It suggest that some sort of borrowing takes place and that it's just an optional check, which isn't the case. It should be named something like "enforced static usage analysis" instead. In my programming language I have similar mechanism. But it isn't just checking, since it affects code generation by tracking which variables are still in use and which can…

In Rust variables are not destroyed after the last borrow ends but instead when it goes out of scope. I guess that is why it us called borrow checking.

> In Rust variables are not destroyed after the last borrow ends but instead when it goes out of scope

That's the problem. Once I had a tricky case, where I locked a mutex in a match expression only to read a single field to match from the mutex contents. In one of branches of the match expression I locked this mutex once again and got a deadlock. Rust compiler wasn't smart enough to realize that the temporary variable for the mutex lock object should be destroyed earlier (it's no longer needed). So, I needed manually reading the field I need into a named variable to eliminate this deadlock.

A more advanced temporaries lifetime analysis would solve problems like described above, but it means basically duplicating a lot of stuff which is already done in the borrow checker (which runs as an afterpass).

Re: A few good ideas in programming languages

#35
post #4
post #2

out (; balance == balance + amount) // checked after method returns How exactly does it work? Is this a typo?

Looks like its a typo :( The correct way to go about this would be to return the new balance and capture the return value in the first part of the out postcondition like: ```D double deposit(double amount) in (amount > 0, "Deposit amount must be positive") out (result; result == balance) { balance += amount; return balance; } ``` My mistake! https://dlang.org/spec/function.html#postconditions

HN does not use markdown code block formatting so this is hard to read. Formatting code blocks is simple, two blank spaces before any line to make it a code block and no need for extra newlines (unlike between paragraphs):

  double deposit(double amount)
    in (amount > 0, "Deposit amount must be positive")
    out (result; result == balance)
  {
    balance += amount;
    return balance;
  }

Re: A few good ideas in programming languages

#36

Earlier quoted context omitted.

You can very likely borrow check in languages that don't have it in the type system. Exactly the way you suggest, as an optional add-in. It's still WIP but in my side project I haven't found cases that can't be handled yet. https://github.com/ityonemo/clr

> Exactly the way you suggest, as an optional add-in No, I don't suggest it, but criticize it. Rust performs its checking as a separate step after actual compilation, which sometimes leads to strange behavior (like borrow errors are shown only after actual compilation errors). I prefer an approach which is integrated with other language mechanisms. > It's still WIP but in my side project I haven't found cases that ca…

I think Pony’s reference capabilities are a better solution for linear types. It’s just part of the language so a violation is simply a type error, not something flagged later during static analysis.

Re: A few good ideas in programming languages

#37
post #10
post #7

How does contract programming differ from refinement types?

Poor man's runtime "dynamic" version. AKA: A much worse version. In advanced cases, you'd need dependent types, but the only place where that almost shows up is in the "amount "Contracts" has been around a long time and has not caught on. That's usually a good sign that better approaches are prevailing. In other words: refinement types are a better solution.

> Poor man's runtime "dynamic" version. AKA: A much worse version.

Contracts don't have to be evaluated dynamically, that's just one way they're implemented. See SPARK/Ada for an example of contracts being used to prove programs statically, not just test them dynamically.

Re: A few good ideas in programming languages

#38
post #29

A genuine question: is the first point (flow typing / type narrowing) a subset of or intersection with or just an alias to SSA (static single assignment)? I'm playing with a small interpreted language implementation that is based on Lua, and have reached a point where I want to implement a single-pass SSA (there is a nice short CS paper on this), but cannot get my head around all the concepts, even if I need proper S…

No. Type systems are unrelated to abstract machines which are unrelated to usability. Type inference/checking happens early in the pipeline. SSA is a way of laying out assembly instructions for an abstract machine. I say abstract because real machines re-assign values to the same addresses over time (which is precisely what 'single' static assignment prescribes against). Once you know which registers your real machin…

My idea was that with single pass, I can build SSA form during AST construction, and use phi-nodes to update type flow info. Then I could use SSA form to prove that I can use certain optimized bytecode instructions when a variable/register is known to be of certain type (I have virtual registers and fat instructions, eg ADD takes 2 sources and destination). Maybe I'm mixing control flow, type flow and SSA. I do not understand where I should stop with the pipeline if I use bytecode/VM.

The paper is: Brandis, Marc M., and Hanspeter Mössenböck. "Single-pass generation of static single-assignment form for structured languages." (https://bernsteinbear.com/assets/img/brandis-single-pass.pdf). It was quite understandable to me. For a deeper dive with proper SSA construction with dominance frontiers I could not find time to dig deeper, many other papers on SSA require focused CS work on them, not practically feasible for a side project. Also, single-pass is a requirement for very fast compilation to bytecode and LSP feedback.

I tried to read TS and Pyright source code, they share the same style of immense files and nested local functions, that was quite a steep wall to understand actual inner workings in detail. Maybe TS implementation in Go will be easier to read, it's on my later TODO list. It's tempting to use AI for help, but I'm quite experienced already with undoing AI work when it takes a wrong direction and I do not notice early.

Re: A few good ideas in programming languages

#39
post #32
post #29

Earlier quoted context omitted.

No. Type systems are unrelated to abstract machines which are unrelated to usability. Type inference/checking happens early in the pipeline. SSA is a way of laying out assembly instructions for an abstract machine. I say abstract because real machines re-assign values to the same addresses over time (which is precisely what 'single' static assignment prescribes against). Once you know which registers your real machin…

Ok. More thoughts. I was trying to see what was special about Crystal in this regard. It seems like if you took any ML or Haskell-like, you'd have type inference. Then you could allow shadowing (Rust-style) meaning the same symbol in the source code would be one variable now, and a different variable later. Then your compiler would need to distinguish x into x1 and x2 so it could track them separately. So yeah, kind…

Yes, a lexical scope with shadowing

Re: A few good ideas in programming languages

#40
If you're serious about doing OO with static typing as well as (of course) mutability, you basically have to have something like flow typing to keep away the circle/ellipse nonsense. (In so far as flow typing is really static typing at all!)
Post reply on HN