| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Control.Monad.Free.Foil.Artifact
Description
Serialisation support for checked units: stored terms, spelling tables, name-range metadata, and relocation.
The machinery here assumes only that a unit's interned constants and its locals (the names its binders bind) occupy disjoint name ranges, and it checks that assumption from the recorded metadata rather than taking it on faith. A stored term is then meaningful verbatim: a local keeps its raw id, and a constant is resolved through a spelling table on load.
One policy that provides the disjointness globally and by construction is to keep constants below zero and locals at or above, which is what the guarded successor allocator protects.
What loading trusts, and what it checks, is the client's decision, and the
functions here supply the checkable facts. checkStoredLayout judges the
recorded ranges and constantRelocation judges the constants, both from
metadata alone, so no stored term is ever walked for checking. Only
relocateConstants walks a term, and only when a constant actually moved.
Synopsis
- data ArtifactError ident
- prettyArtifactError :: (ident -> String) -> ArtifactError ident -> String
- newtype StoredTerm = StoredTerm {}
- storeTerm :: forall (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) (n :: S). Binary (AST binder sig n) => AST binder sig n -> StoredTerm
- decodeStored :: forall (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) (n :: S) ident. Binary (AST binder sig n) => StoredTerm -> Either (ArtifactError ident) (AST binder sig n)
- termSpellings :: forall (n :: S) (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) ident. (Distinct n, CoSinkable binder, Bifoldable sig) => NameMap n ident -> AST binder sig n -> Map RawName ident
- localsOf :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S). (Bifoldable sig, HasNameBinders binder) => AST binder sig n -> [RawName]
- spanOfNames :: [RawName] -> Maybe NameRange
- data StoredLayout = StoredLayout {}
- nameRangeSize :: NameRange -> Int
- nameRangeContains :: NameRange -> RawName -> Bool
- nameRangesOverlap :: NameRange -> NameRange -> Bool
- checkStoredLayout :: StoredLayout -> Map RawName ident -> Int -> Either (ArtifactError ident) ()
- constantRelocation :: forall ident (n' :: S) (old :: S). Ord ident => StoredLayout -> NameRange -> Map RawName ident -> Map ident (Name n') -> Either (ArtifactError ident) (Maybe (NameMap old (Name n')))
- relocateConstants :: forall (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) (n :: S) (n' :: S). Bifunctor sig => NameMap n (Name n') -> AST binder sig n -> AST binder sig n'
Errors
data ArtifactError ident Source #
What the machinery here can report. The type is parametric in the
spelling, as the tables are, and a Functor over it.
Since: 0.4.0
Constructors
| MalformedStoredTerm String | The bytes did not decode. The message is the decoder's. |
| OverlappingRegions NameRange NameRange | The recorded constants and locals ranges share a name. |
| SpellingForLocal RawName | The spelling table names something inside the locals region. |
| WrongDeclarationCount NameRange Int | The constants range does not hold one name per declaration. |
| UnknownConstant ident | A spelling the loading world does not know. |
| ConstantAmongLocals ident RawName | A relocation target inside the locals region, where the verbatim locals could capture it. |
Instances
| Functor ArtifactError Source # | |
Defined in Control.Monad.Free.Foil.Artifact Methods fmap :: (a -> b) -> ArtifactError a -> ArtifactError b # (<$) :: a -> ArtifactError b -> ArtifactError a # | |
| Show ident => Show (ArtifactError ident) Source # | |
Defined in Control.Monad.Free.Foil.Artifact Methods showsPrec :: Int -> ArtifactError ident -> ShowS # show :: ArtifactError ident -> String # showList :: [ArtifactError ident] -> ShowS # | |
| Eq ident => Eq (ArtifactError ident) Source # | |
Defined in Control.Monad.Free.Foil.Artifact Methods (==) :: ArtifactError ident -> ArtifactError ident -> Bool # (/=) :: ArtifactError ident -> ArtifactError ident -> Bool # | |
prettyArtifactError :: (ident -> String) -> ArtifactError ident -> String Source #
Render an error, given a renderer for the spellings.
Since: 0.4.0
Stored terms
newtype StoredTerm Source #
A term as stored: canonical bytes. Equality of stored terms is byte equality, which is what a canonical-artifact property tests.
Since: 0.4.0
Constructors
| StoredTerm | |
Fields | |
Instances
storeTerm :: forall (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) (n :: S). Binary (AST binder sig n) => AST binder sig n -> StoredTerm Source #
Store a term verbatim, through the instances of Control.Monad.Free.Foil.Binary.
The disjoint layout is what makes verbatim enough. A constant's spelling
goes into the unit's table (termSpellings), and a local needs no
table: its id is expected to be canonical, which it is when elaboration
allocates locals in a region of their own.
Since: 0.4.0
decodeStored :: forall (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) (n :: S) ident. Binary (AST binder sig n) => StoredTerm -> Either (ArtifactError ident) (AST binder sig n) Source #
Decode a stored term's bytes: the instances alone, no meaning yet.
Meaning is given per unit, by constantRelocation and
relocateConstants.
Since: 0.4.0
Spelling tables and locals
Arguments
| :: forall (n :: S) (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) ident. (Distinct n, CoSinkable binder, Bifoldable sig) | |
| => NameMap n ident | Spellings of the top-level names. |
| -> AST binder sig n | |
| -> Map RawName ident |
The spelling-table entries a term needs. Its free variables are exactly its constants, provided the stored declaration is closed over everything local. Each is mapped to its spelling from the display table.
Note that the table should cover the referenced constants and only those. A table of everything in scope would let an unused import dirty a dependant's content hash, and would differ between build schedules.
Since: 0.4.0
localsOf :: forall (sig :: Type -> Type -> Type) (binder :: S -> S -> Type) (n :: S). (Bifoldable sig, HasNameBinders binder) => AST binder sig n -> [RawName] Source #
The names a term's binders bind: what a unit's locals range covers.
Note that supportOf cannot see them, since they are bound and not free.
Since: 0.4.0
spanOfNames :: [RawName] -> Maybe NameRange Source #
The tightest range covering the given names, or Nothing for none.
The caller picks its own convention for the empty range.
>>>spanOfNames [7, 3, 5]Just (NameRange {nameRangeLo = 3, nameRangeHi = 7})
Since: 0.4.0
Range metadata and its checks
data StoredLayout Source #
A unit's recorded name layout: the actual names of its own constants, and of its locals. The two travel together, so that they cannot be mixed up with the ranges of the loading world. An artifact records them as one field, and the checks and the relocation consume them as one value.
Since: 0.4.0
Constructors
| StoredLayout | |
Fields | |
Instances
| Binary StoredLayout Source # | |||||
Defined in Control.Monad.Free.Foil.Artifact | |||||
| Generic StoredLayout Source # | |||||
Defined in Control.Monad.Free.Foil.Artifact Associated Types
| |||||
| Show StoredLayout Source # | |||||
Defined in Control.Monad.Free.Foil.Artifact Methods showsPrec :: Int -> StoredLayout -> ShowS # show :: StoredLayout -> String # showList :: [StoredLayout] -> ShowS # | |||||
| Eq StoredLayout Source # | |||||
Defined in Control.Monad.Free.Foil.Artifact | |||||
| type Rep StoredLayout Source # | |||||
Defined in Control.Monad.Free.Foil.Artifact type Rep StoredLayout = D1 ('MetaData "StoredLayout" "Control.Monad.Free.Foil.Artifact" "free-foil-0.4.0-Bfr1HBuIt5nKrF1pl2NAYZ" 'False) (C1 ('MetaCons "StoredLayout" 'PrefixI 'True) (S1 ('MetaSel ('Just "storedConstants") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 NameRange) :*: S1 ('MetaSel ('Just "storedLocals") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 NameRange))) | |||||
nameRangeSize :: NameRange -> Int Source #
How many names a range holds.
>>>nameRangeSize (NameRange 3 5)3
Since: 0.4.0
nameRangeContains :: NameRange -> RawName -> Bool Source #
Whether a raw name lies in a range.
Since: 0.4.0
nameRangesOverlap :: NameRange -> NameRange -> Bool Source #
Whether two ranges share a name. An empty range overlaps nothing.
>>>nameRangesOverlap (NameRange 0 4) (NameRange 4 9)True>>>nameRangesOverlap (NameRange 0 4) (NameRange 5 9)False
Since: 0.4.0
Arguments
| :: StoredLayout | The unit's recorded layout. |
| -> Map RawName ident | Its spelling table. |
| -> Int | Its declaration count. |
| -> Either (ArtifactError ident) () |
The checks a unit's recorded layout admits, judged from metadata alone. The constants and locals ranges must not overlap, no spelling may be recorded for a local, and the constants range must hold exactly one name per declaration, since allocation is dense from the range's low end.
>>>layout = StoredLayout (NameRange (-10) (-9)) (NameRange 0 5)>>>checkStoredLayout layout (Map.fromList [(-20, "P.base")]) 2Right ()>>>checkStoredLayout layout (Map.fromList [(3, "q")]) 2Left (SpellingForLocal 3)
Since: 0.4.0
Relocation
Arguments
| :: forall ident (n' :: S) (old :: S). Ord ident | |
| => StoredLayout | The unit's recorded layout. |
| -> NameRange | The range this run assigned to the unit. |
| -> Map RawName ident | Its spelling table. |
| -> Map ident (Name n') | What each spelling means here. |
| -> Either (ArtifactError ident) (Maybe (NameMap old (Name n'))) |
What a unit's constants need in the loading world, judged once, from
the spelling table alone. Nothing says every constant already has the
id its spelling means here, which is the fast path, on which no term is
walked at all. Otherwise the result is the renaming to apply. Its domain
is a scope of the unit's world, which no longer exists, so its index is
the caller's phantom.
The unit's own constants (table entries inside the recorded range) do not consult the world: they are being loaded right now, in the same order they were allocated, so their relocation is the affine shift between the recorded range and the one this run assigned. Their new names are thereby minted ahead of their allocation, which the caller's trust covers. An imported constant resolves by its spelling, and one this world does not know is reported. Finally, no relocation target may land among the locals, since verbatim locals rest on the two never meeting.
Since: 0.4.0
relocateConstants :: forall (binder :: S -> S -> Type) (sig :: Type -> Type -> Type) (n :: S) (n' :: S). Bifunctor sig => NameMap n (Name n') -> AST binder sig n -> AST binder sig n' Source #
Rename every constant reference through the map, moving the term from
the unit's world into the loading one. This is the restriction to
constants of a general renaming .
Name n -> Name n'sinkabilityProof embodies the general renaming, but its efficient
implementations degenerate the renaming to a coercion under binders,
which is sound only for inclusions, whereas this walk carries an arbitrary
map through.
The invariant that lets the walk ignore the binders is the disjointness
the recorded layout certifies: every name in the map's domain is a
constant, and a binder binds locals, so no binder can shadow a name in
the domain and no local can be in it. Note that this covers imported
constants too, since checkStoredLayout refuses a spelling for any name
among the locals. Thus the map never needs extending under a binder, and
locals and patterns cross by coercion. A constant outside the map (bytes
referencing something the spelling table does not cover) is re-minted
unchanged, trusted like everything else about the term. Note that a map
that is the identity on raw ids would make the whole walk a coercion,
which is why constantRelocation reports it as no relocation at all.
Since: 0.4.0