pure-borrow
Safe HaskellNone
LanguageGHC2021

Data.Record.Linear.Borrow.Experimental.Split

Description

An experimental module for splitting a borrow of a record gradually and partially. To be used when you need to access more than one fields of a borrowed record, and borrow-out each field as you go. If you can just pattern match on the record borrow once, consider using Data.Record.Linear.Borrow.Experimental.PatternMatch.

The API is subject to future change.

Synopsis

Label Type

data RecordLabel (r :: TYPE rep) (field :: Symbol) a Source #

RecordLabel r field a witnesses that the record type r has a field named field of type a. To be used as a label argument for (.#), (-#), (+#), and (!#).

The record label is usually constructed by overloaded labels as `#field` under OverloadedLabels extension.

Instances

Instances details
(HasField field r a, field ~ field') => IsLabel field (RecordLabel r field' a) Source # 
Instance details

Defined in Data.Record.Linear.Borrow.Experimental.Split

Methods

fromLabel :: RecordLabel r field' a #

Single Field Accessor

(.#) :: forall (field :: Symbol) r a (k :: BorrowKind) (α :: Lifetime). Borrow k α r %1 -> RecordLabel r field a -> Borrow k α a infixl 9 Source #

Borrow-level field accessor. record .# #field returns a borrow of the field of the record of the same kind.

{-# LANGUAGE OverloadedLabels #-}
data MyRecord = MyRecord {field :: Ref Int, otherField :: Vector String}

recordBor :: Borrow bk α MyRecord
recordBor = ...

fieldOfRecordBor :: Borrow bk α (Ref Int)
fieldOfRecordBor = recordBor .# #field

otherFieldOfRecordBor :: Borrow bk α (Vector String)
otherFieldOfRecordBor = recordBor .# #otherField

In above example, we annotate type of the divided field borows for clarity, but the type can be inferred by the record type and labels.

For more complex, partial splitting of a record, see Splitting a record borrow into pieces for more detail.

Splitting a record borrow into pieces

Overview

(.#) is handy when you need only one field of a borrowed record, but not applicable when you need to access more than one fields. For that purpose, we provide SplitRecord machinery and associated combinators (-#), (+#), and (!#) for splitting a borrow of a record into borrows of its fields.

In such cases, however, we must ensure that each field of a record borrow is split out at most once. Here, SplitRecord a bk α fs comes int play: it is representationally same as Borrow bk α a, but only the fields in fs remains unsplit. That is, a field is borrowed by splitting combinator only if it remains in fs type parameter, and the field is removed from fs after splitting.

A record type can be converted into a SplitRecord by splitRecord function, which requires SplittableRecord instance for the record type. This can be derived generically by deriving Generic and then SplittableRecord for the record type, as follows:

{-# LANGUAGE TemplateHaskell, DataKinds, TypeFamilies, LinearTypes #-}
import Generics.Linear.TH (deriveGeneric)

data MyRecord = MyRecord {field :: Ref Int, otherField :: Vector String}

deriveGeneric ''MyRecord

deriving anyclass instance SplittableRecord MyRecord

Once we have SplittableRecord instance derived, we can now split a record borrow partially, step-by-step using (-#), (+#), and (!#) combinators. Through out this documentation, suppose we have the following record borrow in scope:

recordBor :: Mut α MyRecord

splitRec ::
  SplitRecord MyRecord Mut α
      '[ '("field", '( 'One, Ref Int)), '("otherField", '( 'One, Vector String))]
splitRec = splitRecord recordBor

Borrows-out a linear field

When you want to borrow-out a single linear field from the record, you can use (-#) combinator, as follows:

fieldBor :: Mut α (Ref Int)
restBor :: SplitRecord MyRecord Mut α '[ '(otherField, '( 'One, Vector String)))]
(fieldBor, restSplit) = splitRec -# #field

Here, the borrow to the field of splitRec is borrowed out as fieldBor, and the remaining borrow is represented by restSplit, where only otherField remain unsplit. We can no longer borrow-out field from restSplit by the type constraints.

Consuming a split record

When you no longer need to borrow any field of a split record, you can just consume a split record, or call (!#) to borrow out a single field from the split record and discard the rest of the record borrow, as follows:

otherFieldBor :: Mut α (Vector String)
otherFieldBor = restSplit !# #otherField

(!#) is analogous to (.#), but it acts on SplitRecord instead of borrow of a record.

APIs

splitRecord :: forall a (bk :: BorrowKind) (α :: Lifetime) (m :: Multiplicity). SplittableRecord a => Borrow bk α a %m -> SplitRecord a bk α (Fields a) Source #

Start subdividing a borrow of a record.

data SplitRecord a (bk :: BorrowKind) (α :: Lifetime) (s :: [(Symbol, (Multiplicity, Type))]) Source #

SplitRecord a bk α fs represents a borrow of a value of type a of borrow kind bk (i.e. Share or Mut) for lifetime α with fs remains unsplit. That is, if the field f is removed by some combinators like (-#), then the resulting SplitRecord will have f removed from the fs type-level list.

At any time, you can consume SplitRecord when the remaining fields are no longer of interest.

Instances

Instances details
Consumable (SplitRecord a bk α fs) Source # 
Instance details

Defined in Data.Record.Linear.Borrow.Experimental.Split

Methods

consume :: SplitRecord a bk α fs %1 -> () #

class All (IsFieldOf a :: (Symbol, (Multiplicity, Type)) -> Constraint) (Fields a) => SplittableRecord a Source #

(-#) :: forall a (field :: Symbol) (fs :: [(Symbol, (Multiplicity, Type))]) x (bk :: BorrowKind) (α :: Lifetime) (m :: Multiplicity). (SplittableRecord a, Lookup field fs ~ 'Just '('One, x)) => SplitRecord a bk α fs %m -> RecordLabel a field x -> (Borrow bk α x, SplitRecord a bk α (Delete field fs)) infix 9 Source #

Splitting a linear field from a borrow of a record. record -# #field returns a pair of the borrow of a split field and remaining split record, where field is removed from the type-level list of the remaining split record.

Mnemonic: (-#) subtracts the field from the record.

(+#) :: forall a (field :: Symbol) (fs :: [(Symbol, (Multiplicity, Type))]) x (bk :: BorrowKind) (α :: Lifetime) (m :: Multiplicity). (SplittableRecord a, Lookup field fs ~ 'Just '('Many, Ur x)) => SplitRecord a bk α fs %m -> RecordLabel a field (Ur x) -> (Ur x, SplitRecord a bk α fs) infix 9 Source #

Skimming the value of a nonlinear (unrestricted) field. record +# #field returns a pair of the value of the field of the record and the original split record. The returned value of field is wrapped by Ur and can be used more than once.

Mnenonic: (+#) you can use nonlinear field more (+) than once.

(!#) :: forall a (field :: Symbol) (fs :: [(Symbol, (Multiplicity, Type))]) x (bk :: BorrowKind) (α :: Lifetime) (m :: Multiplicity). (SplittableRecord a, Lookup field fs ~ 'Just '('One, x)) => SplitRecord a bk α fs %m -> RecordLabel a field x -> Borrow bk α x infix 9 Source #

Extracting the borrow to the single linear field form a borrow of a record. record !# #field returns a borrow of the field of the record, discarding the borrow to the rest of the record.

Mnemonic: (!#) destructs a borrow of a record to that of a single field.