Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add records to the Inferno language #103

Merged
merged 24 commits into from Mar 15, 2024
Merged

Add records to the Inferno language #103

merged 24 commits into from Mar 15, 2024

Conversation

siddharth-krishna
Copy link
Collaborator

@siddharth-krishna siddharth-krishna commented Feb 28, 2024

This PR brings records to the Inferno language.

Syntax:

let r1 = {ht = 1.51; wt = 62.4} in
let bmi = fun r -> r.wt / (r.ht * r.ht) in
bmi r1

Types:

A general record type looks like {f1: t1; ...; fN: tN; 𝜌} where 𝜌 is a type variable called a row variable representing either:

  • : a special symbol denoting no further fields in the record, or
  • one or more fields with types f1': t1'; ...; fN': tN'

The example above has the following types:

r1 : {ht: double; wt: double}
bmi : forall 'a. {ht: double; wt: double; 'a} -> double

Here, the concrete record r1 has type {ht: double; wt: double} (which is syntactic sugar for {ht: double; wt: double; ⏊}, the record having exactly two fields), and the type of the function bmi uses a row variable 'a denotes the fact that it accepts as argument any record that has at least the fields ht and wt.

Differences to OCaml's Immediate Objects

The description above is very similar to OCaml's ad-hoc objects, except that we retain the row variables in the types and show it to the user. The example above in OCaml would have the types:

r1 : {ht: double; wt: double}
bmi : {ht: double; wt: double; ...} -> double

I decided to keep the row variables explicit for now, because it makes the quantification on the "rest of the record fields" more explicit when we manually annotate functions. We can always change the pretty-printer and parser later to be like OCaml and hide row variables using the ... syntax, if we prefer.

Copy link
Collaborator Author

@siddharth-krishna siddharth-krishna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@goodlyrottenapple if you ever have a few moments this week, I'd love to get your thoughts on the changes to the type inference. I've mentioned you on some comments with some particular questions. Thanks!

inferno-core/src/Inferno/Infer.hs Outdated Show resolved Hide resolved
where
st = (emptySubst, cs)

unifyMany :: [TypeError SourcePos] -> [InfernoType] -> [InfernoType] -> Solve Subst
unifyMany :: [TypeError SourcePos] -> [InfernoType] -> [InfernoType] -> SolveState Int Subst
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once everything works, I can clean this up and use some FreshVarState or something instead of Int

inferno-core/src/Inferno/Infer.hs Outdated Show resolved Hide resolved
inferno-core/src/Inferno/Instances/Arbitrary.hs Outdated Show resolved Hide resolved
inferno-core/src/Inferno/Parse.hs Outdated Show resolved Hide resolved
@@ -1435,23 +1519,30 @@ prettyPrec isBracketed prec expr =
<> elsePretty
Op e1 _ _ (n, NoFix) ns (Ident op) e2 ->
bracketWhen e2 (prec > n) $
prettyOpAux (n + 1) e1 <> (if hasTrailingComment e1 then hardline else mempty)
prettyOpAux (n + 1) e1
<> (if hasTrailingComment e1 then hardline else mempty)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these are also formatting changes...

inferno-types/src/Inferno/Types/VersionControl.hs Outdated Show resolved Hide resolved
@@ -42,6 +44,7 @@ data Value custom m
| VEnum VCObjectHash Ident
| VArray [Value custom m]
| VTuple [Value custom m]
| VRecord (Map.Map Ident (Value custom m))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. It reminds me of the evaluation environment!

@siddharth-krishna
Copy link
Collaborator Author

@Daniel-Diaz I found some bugs in the PR, so I'm pushing a few new commits with some extra tests and the respective bug fixes. Hope you don't mind reviewing the new commits in the same PR so I can merge it in in a good state. :)

The changes are:

  • Add the new record constructors of Expr and InfernoType to the Arbitrary instances, so that they are covered by the QuickCheck tests
  • Fix the pretty printing of records to pass the pretty-printer-and-parser-are-inverses test
  • Fix the fresh variable counter in inferTypeReps as suggested by Sam
  • Add tests for the above

I also updated the PR description with details of the new record syntax and types.

@siddharth-krishna siddharth-krishna marked this pull request as ready for review March 12, 2024 13:23
Copy link
Contributor

@Daniel-Diaz Daniel-Diaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@siddharth-krishna siddharth-krishna merged commit c180370 into main Mar 15, 2024
1 check passed
@siddharth-krishna siddharth-krishna deleted the sidk-records branch March 15, 2024 07:21
siddharth-krishna added a commit that referenced this pull request Mar 26, 2024
This PR follows up on #103 by adding pattern matching for records. E.g.
```ocaml
let f = fun r -> match r with { | {x = x; y = y} -> x + y } in f {x = 3.3; y = 5.1}
```

It also adds a type error when record literals or patterns use duplicate
field names.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants