Live data from Hacker News

Is your programming language unreasonable? (2015)

fsharpforfunandprofit.com

91–100 of 138 posts

Re: Is your programming language unreasonable? (2015)

#91

The article goes through a series of examples to show motivations for the following: 1. Variables should not be allowed to change their type. 2. Objects containing the same values should be equal by default. 3. Comparing objects of different types is a compile-time error. 4. Objects must always be initialized to a valid state. Not doing so is a compile-time error. 5. Once created, objects and collections must be immu…

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…

Mutability is an abstraction, it doesn't forbid in place modification of data. What it forbids is other code accessing data that holds references the array prior to the modification, which creates a logical error.

Re: Is your programming language unreasonable? (2015)

#92
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…

No. In languages like F#, you can encode in the type system that the result can be either a customer or an error. And the compiler won't let you "access" the customer until you've safely checked that the result is, indeed, the customer rather than an error.

In this way you end up encoding more intent within your types and behaviours rather than implicitly letting the consumer figure it out themselves.

Re: Is your programming language unreasonable? (2015)

#93
post #43

Earlier quoted context omitted.

I really can't work out what you're trying to say here. I can guess, but maybe it would be better if you wrote something more expository or discursive. For example, is "Speakeasy, Lightouch, Jazz" intended to be a single noun? A single thing? Or is it a collection? A sequence? What do you mean by "getting stable shelter"? Or to "hand off notes to people"? Have you written something already? is there a link?

I'm assuming that comment was written by a bot, tbh.

As it happens, it wasn't, it's a real person.

Re: Is your programming language unreasonable? (2015)

#94
post #13

> I don’t care what my language will let me do, I care more about what my language won’t let me do. I think this is the best summary of the article. In short, please protect me from my own stupidity and/or laziness.

I like how this could be taken as an endorsement or criticism of the article, depending on the hubris of the reader

I find that these sorts of polarizing statements which either side can take to mean a one-up on the other side tend to get lots of upvotes on applicable forums.

Re: Is your programming language unreasonable? (2015)

#95
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?

I'm not familiar with most languages, but I can state that Rust requires matching sum types to cover all possible variants. For example:

    enum Animal {
        Dog,
        Cat,
        Pigeon
    }
    let pet = Animal::Dog;
    let sound = match pet {
        Animal::Dog => String::from("ouaf"),
        Animal::Cat => String::from("miaou")
    };
This would not compile, because you've forgotten to cover the pigeon. You would need to either add one of the two lines below:

    Animal::Pigeon => String::from("rou rou")
or

    _ => String::from("eeeeeeeeeeeei")
The last line is a catch all, which would cover anything not checked for explicitly.

Re: Is your programming language unreasonable? (2015)

#96
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…

No. In languages like F#, you can encode in the type system that the result can be either a customer or an error. And the compiler won't let you "access" the customer until you've safely checked that the result is, indeed, the customer rather than an error. In this way you end up encoding more intent within your types and behaviours rather than implicitly letting the consumer figure it out themselves.

Adding to this, it's nice to have the compiler enforce these things rather than rely on the dev to see the variable name and treat it accordingly. In the latter case, you're one sleepy afternoon away from having things blow up in production because you "thought there was no way that that variable could be a null pointer"

Re: Is your programming language unreasonable? (2015)

#97

Earlier quoted context omitted.

I mean for a programming language to be able to handle the general cases it should also handle the maybe 10% or 20% of programs that might need mutation in their hot paths. Obviously one solution is to call out to a different programing language for only the areas that require it, but I’d consider that a definite deficiency if it’s commonly required. I think e.g C# and F# handle mutation well, but I think F# does the…

"Depends on what problems you are solving I suppose. " - well, yes. Hence my question about what you're defining as the general case; that 10-20% seems rather high for cases you'd need mutability. I think it's a bit of a red herring to ask "can you solve them quickly enough" - after all, if pure speed across all use cases is your concern, why would you advocate a garbage collected language?

I’d only advocate using GC’d languages as a general (ie covering as many use cases as possible in one language - this isn’t even necessarily a good idea to begin with!) where it’s easy to dodge it. For example it must be easy and reasonably idiomatic controlling what is and isn’t heap allocated.

Obviously all languages are a tradeoff between ergonomics and performance, but it should be easy and idiomatic to use the back doors. Using mutation in F# or stack allocation in C# doesn’t feel like swimming upstream. If the language offers a comfortable experience for the 80-90% case and a not-terrible experience for the rest, that’s very good. If the “back door” is e.g. JNI or being forced to use SoA when you wanted AoS in Java that’s not very impressive.

I grabbed the 10-20% number of “programs that need mutation” out of my behind obviously, but it is highly subjective.

Re: Is your programming language unreasonable? (2015)

#98

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…

Yes, Haskell as a pure functional language does this too. A naive copy-by-value handling of lists will usually end up in the same order of magnitude for performance as mutate-in-place linked lists in C. The compiler can track those immutable values and just mutate them in place, when it can guarantee that's a safe operation. The vast majority of the time, you can get away with just copying a pointer or renaming, not the whole variable.

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.)

Re: Is your programming language unreasonable? (2015)

#99

> Objects containing the same values should be equal by default. I strongly disagree with this. I’d take it one step further and say that there should be no equality operator or default equals function for non-primitive data types. Some specific type of records/tuples could use a default equality, but it should be opt-in or follow from using metadata like an attribute (e.g Rust derive) or only for those specific type…

To me "avoiding mutations" does not mean "no mutations", but rather "no mutations in-between states in a state machine". The system should model state as an immutable snapshot, while mutations when forming that state are completely acceptable.

Re: Is your programming language unreasonable? (2015)

#100
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…

Typescript allows this

  const c = getCustomer();  // type is Customer | null
  const z : Customer = c;   // ERROR; incompatible types
  if (x === null) {
    // handle null
  } else {
    const y : Customer = c; // OK; knows c isn't null here
  }
Post reply on HN