reasonml / reason

Simple, fast & type safe code that leverages the JavaScript & OCaml ecosystems
http://reasonml.github.io
MIT License
10.13k stars 428 forks source link

Unifying 'ref' and 'mutable'? #356

Closed yunxing closed 2 months ago

yunxing commented 8 years ago

From a beginner's perspective, I find the "mutable" keyword and "ref" keyword are pretty confusing: What's the difference between using {mutable field: int} and {field: int ref} ? It looks like they are expressing the same intention to have a mutable field.

I'm thinking if we can unify these two by the following proposal:

jberdine commented 8 years ago

{mutable field: int} and {field: int ref} are significantly different. Consider what {field: int ref} looks like after expanding the definition of ref:

type 'a ref = {mutable contents: 'a}

{field: {mutable contents: int}}

So there is an additional reference/pointer in the ref case: a value of type {mutable field: int} is a pointer to an block of memory that contains an int, where the int may change, a value of type {field: {mutable contents: int}} is a pointer to a block of memory that contains a constant pointer to another block of memory that contains an int, where the int may change.

If the existence of both forms is confusing, I would say to just remove ref, or hide it somewhere beginners are not likely to find.

jordwalke commented 8 years ago

If the existence of both forms is confusing, I would say to just remove ref, or hide it somewhere beginners are not likely to find.

This is exactly the approach we've taken in the docs. :D

In fact, I've eliminated ref in my own code. I always just use a record with field contents.

jordwalke commented 8 years ago

I think if there were a second syntax for mutation that we encouraged, it could still be a {contents} behind the scenes, but one that is more intuitive. @jberdine, does the following seem easy to implement?

let x = "hey I'm a regular let binding";
var y = "I'm a faux mutable binding";

if (someCondition) {
   y = "new Value";
};
callOutToSomeFunction y;

Which would be converted to:

let x = "hey I'm a regular let binding";
let y = {contents: "I'm a faux mutable binding"};

if (someCondition) {
   y.contents = "new Value";
};
callOutToSomeFunction y.contents;
yunxing commented 8 years ago

oh, I guess I missed one thing: When you write callOutToSomeFunction y Our parser would need to be smart enough to tell we are reading from a reference and automatically convert it to callOutToSomeFunction y.contents.

jordwalke commented 8 years ago

Yes, that is correct - it should be trivially lexical though (not involving any type system work, or dependencies between two files).

bslatkin commented 8 years ago

The hard thing for me, as a newbie, is how the ref type is the first introduction to mutability in beginner texts like Real World OCaml. There it tells you to use ! and := to read and write refs. That doesn't really fit with Reason's more C-like syntax. So it'll probably be hard to hide it from beginners.

It feels sloppy to access foo.contents for refs in Reason. I like the idea of using var as sugar around a single-field record. I think the simple rule of "if foo was declared as a var, then replace all occurrences of foo with foo.contents" will achieve what you want?