Open andrevidela opened 1 month ago
Note that Agda also gives you access to these definitions as some sort of defined projections out of the record.
open import Agda.Builtin.Nat
record ANat : Set where
constructor mkANat
field
theNat : Nat
itsSuc : Nat
itsSuc = suc theNat
open ANat public
update : ANat → ANat
update n = record n { theNat = itsSuc n }
Is this a feature of records or is this a feature of modules? I assumed it was the latter.
edit: After some thinking I've come to the conclusion that we can probably hack it (obtain the value from the elaborated top-level definitions, pass around the context appropriately, etc), but I'd say it's out of scope for this specific feature.
It's a feature of the way we desugar record declarations into modules of mutual definitions so I'd say the interpretation is pretty open-ended.
I agree the current feature is useful as-is; just highlighting that the comparison with Agda can be deceiving.
Summary
Allow
let
bindings in the body of record definitions.Motivation
Records abstract over a lot of boilerplate over using
data
. However, they are limited in ways thatdata
isn't. One thing thatdata
declarations allow butrecord
do not is the ability to writelet
-binders in the type of a constructor. For example:This is currently impossible with a record. And in itself it's not too problematic, but
data
does not synthesize projections functions, unlike records. This proposal fill the hole in the design space by providing the ability to writelet
in record defintions.The proposal
Allow
let
blocs in the body of records that allow definitions to be reused multiple times in subsequent fields.For typechecking reasons, the
let
blocks in records only allow clauses with type information. This is because eachlet
binder must be lifted to the top-level where type inference is brittle and because the types of each field must be easily computable, we require each field to be given a typeExamples
The block of definitions can be written after the
let
:let
blocks can be interleaved with fields:Technical implementation
Thankfully, for everyone's sanity, this can be implemented as a desugaring step. Any
let
block is translated to alet…in…
when the record is converted todata
:is desugared to
Which itself is converted to a top-level definition:
Interleaved
let
-blocks result in nestedlet…in…
:Becomes
Alternatives considered
Agda-style
Agda provides the same feature but the syntax is "flipped":
that is, the fields need to occur in a
field
block and the bound definitions appear in the body of the record.Because this is a breaking change and does not bring new functionality, there is no reason to do this. However, it allows let bound definitions to be accessed via record projections. This specific feature is out of scope for this proposal in Idris.
Conclusion
This bring feature parity between
record
anddata
while keeping the ergonomic benefits ofrecord
syntax. By ensuring it's merely a desugaring step it does not introduce additional typechecking challenges.A prototype implementation is available here https://github.com/andrevidela/Idris2/tree/let-record-body