Live data from Hacker News

Is your programming language unreasonable? (2015)

fsharpforfunandprofit.com

121–130 of 138 posts

Re: Is your programming language unreasonable? (2015)

#121

Earlier quoted context omitted.

Rust's borrow checker would prevent Example 5 from compiling, since once you add `cust` to the collection, you can't touch it anymore (unless you insert a clone or etc.). So in this case at least, the inability to reason about code can be resolved by banning mutable aliasing, without eliminating mutability.

Rust's ownership system would prevent example 5 from working (because you have to move the instance into the set). The borrow checker is about validating that references don't outlive their target, and R^W.

Oops.

Re: Is your programming language unreasonable? (2015)

#122

Earlier quoted context omitted.

5. Once created, objects and collections must be immutable. So this language would not be general purpose, as it would not be suitable for high-performance computing. Large scale simulations almost always involve arrays that are modified in place. Being able to somehow declare a collection to be immutable would be highly useful, but not having the option of mutable collections limits the kinds of problems that can be…

Isn’t it possible (at least in theory) to make mutability an implementation detail of the compiler/runtime? Rust’s borrow checker approaches this, but the abstraction leaky or nonexistent. Additionally, many high performance computing applications (e.g. Tensorflow) abstract away expensive mutable operations, so at least in theory, it should be possible to isolate mutability to small segments of code where mutability…

Rust's im[1] and rpds[2] crates are refcounted pointers to immutable data structures, but support mutable operations on &mut instances. When an instance is cloned, it merely creates another pointer. When an instance is modified, it uses Arc::make_mut() to only clone each tree node if it has other users. This approach has runtime overhead, but makes nested updates (foo[0][0].attr = 1) as simple as mutable structures.

This somewhat resembles immer.js (uses a proxy around an immutable structure which records updates). Contrast this approach to Clojure transients (whose children don't magically become transient), and whatever Haskell does (https://news.ycombinator.com/item?id=24740384).

[1]: https://docs.rs/im/

[2]: https://docs.rs/rpds/

Re: Is your programming language unreasonable? (2015)

#123
post #70
post #61

Earlier quoted context omitted.

1. Variables should not be allowed to change their type. This sounds nice, but is there a way to accomplish it without losing some expressibility or concision? Rather than looking at JS, consider low-level operations on a small chunk of memory as a niche example. Interpreting the same region as a buffer of 64-bit ints vs 16-bit uints gives entirely different behavior to the standard operators like addition, multiplic…

In descendants of ML (Haskell, OCaml, Rust etc) you can use Algebraic Data Types to condense your wall of methods to one function

Algebraic data types have runtime case information, and won't let you reinterpret the underlying bits of a binary buffer between types. I think grandparent meant they wanted pointer casts, unions, reinterpret_cast, or transmute.

Re: Is your programming language unreasonable? (2015)

#124
post #90

In example #6, he gives this as the unreasonable approach: var repo = new CustomerRepository(); var customer = repo.GetById(42); Console.WriteLine(customer.Id); with the issue being customer can be null, which is not being accounted for. The reasonable approach he says is to use a sum type: var repo = new CustomerRepository(); var customerOrError = repo.GetById(42); if (customerOrError.IsCustomer) Console.WriteLine(c…

> Do most (or any) languages in which people take this approach actually enforce handling of all cases? Or could a programmer write that this way: [...]

Any language that provides this sort of union type will error in some way if you try to access something that isn’t guaranteed to be on every type in the union. Some languages which support null checking will infer Customer to be nullable.

Re: Is your programming language unreasonable? (2015)

#125
post #111

Earlier quoted context omitted.

> The caveat is that, in my experience, it's a fair bit harder to reason about performance, as the execution model is even more abstracted away from the hardware than even something like the C model is (which is no longer a good fit either, in this era of speculative execution and multi-level caches.) One solution is to have a tool developed and distributed along with the compiler (so it can never fall out of sync wi…

I think if performance is part of the requirements of your code, then performance must be a part of your type signature. For example, a tail-recursive function needs to have it’s type as tail-recursive.

This is where linear types and in general quantitative type theory comes into play. Also eagerness / laziness annotations.

Tail recursion is not necessary to annotate imo, but I guess the compiler/linter could maybe complain if it finds recursion it can't do a tail call optimisation for. These kinds of warnings are similar to mutable languages warning about things that are probably bad but sometimes necessary.

Re: Is your programming language unreasonable? (2015)

#126
post #107
post #58

Earlier quoted context omitted.

I'm not going to claim that mutability is never useful for performance, but many large scale simulations can be expressed quite elegantly using bulk operations on arrays or other structures, with no mutability in sight. Both particle simulations a la n-body and stencil operations are in this category. An efficient low-level implementation of such bulk operations involves mutable updates, just like any functional lang…

Interesting. Can you explain, with a somewhat simple example, how this can be efficiently implemented, or at all? I mean preserving the appearance of immutability at the source language level, while mutating the original structure under the hood for performance.

Not very knowledgable on this myself, unfortunately, but I believe that in graphics programming, shaders written in GLSL often take the form of a series of functional, mathematical transformations of vertices. Those transforms are run in the GPU as highly parallelized array operations, probably using a lot of mutable state. But those details are mostly hidden from the shader programmer.

Re: Is your programming language unreasonable? (2015)

#127
> The fundamental paradigm of OO (object-identity, behavior-based) is not compatible with “reasonability”, and so it will be hard to retrofit existing OO languages to add this quality.

This is false, I knew this was false even without reading what he meant by "reasonability" and, upon reading what he meant, I confirmed that it is, indeed, false.

Something that I too found odd is the separation between "functional" and... "Mainstream". That got me confused, LISP is over sixty years old and anybody with more than a passing education in computer science knows about it; there are few languages more mainstream than LISP.

I just don't understand this flamewar; terrible code in Clojure is no easier to read than terrible code in Python. Conversely, good code in either language is no easier to reason about than good code in the other.

Re: Is your programming language unreasonable? (2015)

#128
post #107
post #58

Earlier quoted context omitted.

I'm not going to claim that mutability is never useful for performance, but many large scale simulations can be expressed quite elegantly using bulk operations on arrays or other structures, with no mutability in sight. Both particle simulations a la n-body and stencil operations are in this category. An efficient low-level implementation of such bulk operations involves mutable updates, just like any functional lang…

Interesting. Can you explain, with a somewhat simple example, how this can be efficiently implemented, or at all? I mean preserving the appearance of immutability at the source language level, while mutating the original structure under the hood for performance.

Thanks to all who replied.

Re: Is your programming language unreasonable? (2015)

#129
post #35

"Objects containing the same values should be equal by default." No. This breaks down as soon as you have references to other objects in your objects. If you compare by reference, you probably don't get what you want. If you compare by value, you need to go arbitrarily deep, which is not a sane default, because of possible reference cycles. You need a distinction between objects and value types. C# has that (record t…

Accountants do not use erasers. Mutating variables in-place is the training wheels. Got a bank account? Want me to transfer money by increasing this account and decreasing that account? But perhaps that's too small an example. What about a large, distributed system? Both Paxos and Raft are recipes for clusters of machines to agree on immutable sequences of values.

Fun fact my first accounting job we had to use pencils so we could erase and change the figures if we ever got audited.

I left shortly after being taught this business practice but sure makes a great anecdote.

Re: Is your programming language unreasonable? (2015)

#130

Earlier quoted context omitted.

I think if performance is part of the requirements of your code, then performance must be a part of your type signature. For example, a tail-recursive function needs to have it’s type as tail-recursive.

This is where linear types and in general quantitative type theory comes into play. Also eagerness / laziness annotations. Tail recursion is not necessary to annotate imo, but I guess the compiler/linter could maybe complain if it finds recursion it can't do a tail call optimisation for. These kinds of warnings are similar to mutable languages warning about things that are probably bad but sometimes necessary.

It’s neccessary to annotate tail recursion because you are making it clear to the compiler that your initial assumption about the performance of this function is that it will not explode the stack.

The reason it must be made explicit is because when somebody else comes later on to change that function they may miss the fact that it doesn’t explode only because it’s tail-recursive.

You could of course document the requirement - but why document if you can make it a compiler option? “I don’t want this to compile unless I get the behaviour I expect from it”.

Also as far as I am aware C-style functions can not be tail-recursive because they can not clean up the stack after themselves, thus you can’t support tail-recursion across FFI.

Post reply on HN