diku-dk / futhark

:boom::computer::boom: A data-parallel functional programming language
http://futhark-lang.org
ISC License
2.4k stars 165 forks source link

Add functional record update #313

Closed athas closed 7 years ago

athas commented 7 years ago

See discussion in #113.

I'm leaning towards {rv, x = 42, c= true} as the best one, but with an extension: instead of a field-value pair, one can put an arbitrary expression evaluating to a record, and the fields of that record will be included.

The big question: should leftmost or rightmost records take precedence? This affects whether setting the field x to 42 in rv should be written {rv, x=42} or {x=42, rv}.

athas commented 7 years ago

Summary for @melsman to comment.

The expression grammar is extended with the following construct (in EBNF):

exp: "{" (exp | fieldid "=" exp) ("," [exp | fieldid "=" exp])* "}"

This means that a record expression is now a comma-separated sequence of either "field=value" pairs, or an arbitrary expression. In the former case, the given field of the record being constructed will have the indicated value. In the latter case, the expression must evaluate to a record, and the fields of that record are added to the record being constructed. In case a field is mentioned several times, the right-most takes priority.

For example, we can do record concatenation: {fv1, fv2} contains the union of the fields in fv1 and fv2.

Functional record update is just a special case: {fv1, x = 2}.

I like the simplicity and generality of this approach.

melsman commented 7 years ago

The biggest problem I see with the record update syntax

let r1 = {...} in let r2 = {...} in {r1, r2}

is that typing will succeed for all possible records r1 and r2, as I understand the previous discussion. Only an external type constraint will force the resulting record to be well-typed and it may even be that fields in r1 are overwritten (also wrt types) by the r2-binding. If instead there were two different syntactic constructs for catenation and record-update, the programmer would better be able to express the intensions, which would lead to a type error popping up closer to where the mistake is...

On Fri, Mar 3, 2017 at 1:46 PM, Troels Henriksen notifications@github.com wrote:

Summary for @melsman https://github.com/melsman to comment.

The expression grammar is extended with the following construct (in EBNF):

exp: "{" (exp | fieldid "=" exp) ("," [exp | fieldid "=" exp])* "}"

This means that a record expression is now a comma-separated sequence of either "field=value" pairs, or an arbitrary expression. In the former case, the given field of the record being constructed will have the indicated value. In the latter case, the expression must evaluate to a record, and the fields of that record are added to the record being constructed. In case a field is mentioned several times, the right-most takes priority.

For example, we can do record concatenation: {fv1, fv2} contains the union of the fields in fv1 and fv2.

Functional record update is just a special case: {fv1, x = 2}.

I like the simplicity and generality of this approach.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/HIPERFIT/futhark/issues/313#issuecomment-283944513, or mute the thread https://github.com/notifications/unsubscribe-auth/ABHRu2RdScS4kKKtH02l5IazSl_dwGdwks5riAu3gaJpZM4MPwle .

athas commented 7 years ago

I've also considered that problem. I don't think it's a large enough problem to avoid this solution. For two main reasons:

  1. Right now, we can't have partial record types the way we have in SML. Also, all function parameters must have an explicitly assigned type. This prevents the error from propagating too wildly, as I've noticed can easily happen in un-annotated SML programs.

    1. When we do finally get proper type inference and/or partial record patterns, any confusion can be cleared up with improved compiler error messages. GHC has a very nice feature where the inferred types of relevant bindings are printed along with the type error - this is very useful for seeing where the inference went off the rails.

There is another problem I thought of, which is that a programmer may forget that record construction is right-biased, and end up writing {x = 2, rv}. Fortunately, this is easy to detect and warn about in the type checker. A more tricky case is if the programmer writes {rv2, rv1} but really intended {rv1, rv2}. We can't read minds. But this case only occurs when the programmer does not use explicit record assignment, and would occur no matter which convention we pick.