| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Control.Monad.Borrow.Pure
Description
This module is meant to be the prelude module of Pure Borrow, a Rust-style borrow realization in Linear Haskell. This module provides only the basic pieces of the API, and you may want to import other modules, e.g. Control.Monad.Borrow.Pure.BO, Data.Ref.Linear.Borrow, or Data.Vector.Mutable.Linear.Borrow, for more utilities.
Synopsis
- data BO (α :: Lifetime) a
- runBO :: Linearly %1 -> (forall (α :: Lifetime). BO α (After α a)) %1 -> a
- runBOLend :: Linearly %1 -> (forall (α :: Lifetime). BO α (Lend α a)) %1 -> a
- runBO_ :: Linearly %1 -> (forall (α :: Lifetime). BO α a) %1 -> a
- srunBO :: forall (β :: Lifetime) a. (forall (α :: Lifetime). BO (α /\ β) (After α a)) %1 -> BO β a
- srunBO_ :: forall (β :: Lifetime) a. (forall (α :: Lifetime). BO (α /\ β) a) %1 -> BO β a
- data Lifetime
- type (/\) = '(:/\)
- class (α :: Lifetime) <= (β :: Lifetime)
- type (>=) (α :: Lifetime) (β :: Lifetime) = β <= α
- type Static = 'Static
- neverEnds :: (HasCallStack, End Static) => a
- upcast :: a <: b => a %1 -> b
- class (a :: k) <: (b :: k1)
- data Linearly
- linearly :: Movable a => (Linearly %1 -> a) %1 -> a
- class LinearOnly (a :: TYPE rep)
- withLinearly :: LinearOnly a => a %1 -> (Linearly, a)
- askLinearly :: forall (α :: Lifetime). BO α Linearly
- asksLinearly :: forall r (α :: Lifetime). (Linearly %1 -> r) %1 -> BO α r
- asksLinearlyM :: forall (α :: Lifetime) r. (Linearly %1 -> BO α r) %1 -> BO α r
- parBO :: forall (α :: Lifetime) a b. BO α a %1 -> BO α b %1 -> BO α (a, b)
- type Mut = Borrow 'Mut
- type Share = Borrow 'Share
- type Lend = Alias 'Lend
- type Borrow (bk :: BorrowKind) = Alias ('Borrow bk)
- data Alias (ak :: AliasKind) (α :: Lifetime) a
- borrowM :: forall a (α :: Lifetime). a %1 -> BO α (Mut α a, Lend α a)
- borrowLinearlyM :: forall a (α :: Lifetime). (Linearly %1 -> a) %1 -> BO α (Mut α a, Lend α a)
- share :: forall (k :: BorrowKind) (α :: Lifetime) a. Borrow k α a %1 -> Ur (Share α a)
- reborrowing' :: forall (α :: Lifetime) a (α' :: Lifetime) r. Mut α a %1 -> (forall (β :: Lifetime). Mut (β /\ α) a %1 -> BO (β /\ α') (After β r)) %1 -> BO α' (r, Mut α a)
- reborrowing :: forall (α :: Lifetime) a (α' :: Lifetime) r. Mut α a %1 -> (forall (β :: Lifetime). Mut (β /\ α) a %1 -> BO (β /\ α') r) %1 -> BO α' (r, Mut α a)
- (<%~) :: forall (α :: Lifetime) a (α' :: Lifetime) r. (forall (β :: Lifetime). Mut (β /\ α) a %1 -> BO (β /\ α') r) %1 -> Mut α a %1 -> BO α' (r, Mut α a)
- reborrowing_ :: forall r (α :: Lifetime) a (α' :: Lifetime). Consumable r => Mut α a %1 -> (forall (β :: Lifetime). Mut (β /\ α) a %1 -> BO (β /\ α') r) %1 -> BO α' (Mut α a)
- (<%=) :: forall (α :: Lifetime) a (α' :: Lifetime). (forall (β :: Lifetime). Mut (β /\ α) a %1 -> BO (β /\ α') ()) %1 -> Mut α a %1 -> BO α' (Mut α a)
- sharing' :: forall (α :: Lifetime) a (α' :: Lifetime) r. Mut α a %1 -> (forall (β :: Lifetime). Share (β /\ α) a -> BO (β /\ α') (After β r)) %1 -> BO α' (r, Mut α a)
- sharing :: forall (α :: Lifetime) (α' :: Lifetime) a r. Mut α a %1 -> (forall (β :: Lifetime). Share (β /\ α) a -> BO (β /\ α') r) %1 -> BO α' (r, Mut α a)
- (<$~) :: forall (α :: Lifetime) a (α' :: Lifetime) r. (forall (β :: Lifetime). Share (β /\ α) a -> BO (β /\ α') r) %1 -> Mut α a %1 -> BO α' (r, Mut α a)
- sharing_ :: forall (α :: Lifetime) (α' :: Lifetime) a r. Consumable r => Mut α a %1 -> (forall (β :: Lifetime). Share (β /\ α) a -> BO (β /\ α') r) %1 -> BO α' (Mut α a)
- (<$=) :: forall (α :: Lifetime) a (α' :: Lifetime). (forall (β :: Lifetime). Share (β /\ α) a -> BO (β /\ α') ()) %1 -> Mut α a %1 -> BO α' (Mut α a)
- newtype After (α :: Lifetime) a = After (End α => a)
- reclaim' :: forall (α :: Lifetime) a. Lend α a %1 -> After α a
- reclaim :: forall (α :: Lifetime) a. End α => Lend α a %1 -> a
- pureAfter :: forall (α :: Lifetime) a. (End α => a) %1 -> BO α (After α a)
- class End (α :: Lifetime)
- modifyBO :: a %1 -> Linearly %1 -> (forall (α :: Lifetime). Mut α a %1 -> BO α r) %1 -> (r, a)
- modifyBO_ :: a %1 -> Linearly %1 -> (forall (α :: Lifetime). Mut α a %1 -> BO α ()) %1 -> a
- modifyLinearOnlyBO :: LinearOnly a => a %1 -> (forall (α :: Lifetime). Mut α a %1 -> BO α r) %1 -> (r, a)
- modifyLinearOnlyBO_ :: LinearOnly a => a %1 -> (forall (α :: Lifetime). Mut α a %1 -> BO α ()) %1 -> a
- joinMut :: forall (bk :: BorrowKind) (α :: Lifetime) (β :: Lifetime) a. Borrow bk α (Mut β a) %1 -> Borrow bk (α /\ β) a
- joinLend :: forall (α :: Lifetime) a. Lend α (Lend α a) %1 -> Lend α a
- coerceShare :: forall b (α :: Lifetime) a. Coercible a b => Share α a %1 -> Share α b
- class Copyable a where
- copy :: forall (bk :: BorrowKind) (α :: Lifetime). Borrow bk α a %1 -> a
- copyMut :: forall a (α :: Lifetime). Copyable a => Mut α a %1 -> Ur a
- class Clone a where
- splitPair :: forall (ak :: AliasKind) (α :: Lifetime) a b. Alias ak α (a, b) %1 -> (Alias ak α a, Alias ak α b)
- splitEither :: forall (ak :: AliasKind) (α :: Lifetime) a b. Alias ak α (Either a b) %1 -> Either (Alias ak α a) (Alias ak α b)
- split :: forall f x (ak :: AliasKind) (α :: Lifetime). DistributesAlias f => Alias ak α (f x) %1 -> f (Alias ak α x)
- class DistributesAlias (f :: Type -> Type)
- type GenericDistributesAlias (f :: Type -> Type) = (Generic1 f, GDistributeAlias (Rep1 f))
- genericSplit :: forall f x (ak :: AliasKind) (α :: Lifetime). GenericDistributesAlias f => Alias ak α (f x) %1 -> f (Alias ak α x)
- class Consumable a where
- consume :: a %1 -> ()
- class Consumable a => Dupable a where
- dupR :: a %1 -> Replicator a
- dup2 :: a %1 -> (a, a)
- dup :: Dupable a => a %1 -> (a, a)
- dup3 :: Dupable a => a %1 -> (a, a, a)
- class Dupable a => Movable a where
- data Ur a where
Documentation
Pure Borrow: An Overview
This module provides the main API of Pure Borrow, the pure realization of Rust-style borrowing in Linear Haskell.
The core idea is that mutable resources are accessed through lifetime-indexed borrows:
Linearlyproves that we are in a context where linear resources can be allocated and used safely.is a computation that may use borrows valid during the lifetimeBOα aα. It also provides pure API with the concurrency primitive.is a mutable borrow of anMutα aavalid duringα.is an immutable borrow of anShareα aavalid duringα.is the capability to recover the originalLendα aaafterαends.describes post-processing that runs afterAfterα aαends, such as reclaiming aLend.
Examples
You need the following language extensions to use this module:
- BlockArguments
- LinearTypes
- NoImplicitPrelude
- ImpredicativeTypes
- QualifiedDo
...and import these modules:
import Prelude.Linear import Control.Monad.Borrow.Pure import qualified Data.Vector.Mutable.Linear.Borrow as VL import Control.Syntax.DataFlow qualified as DataFlow import Control.Functor.Linear qualified as Control
The examples use qualified do-notation. DataFlow.do is convenient for rebiding pure values, and Control.do is the do-notation for linear functors and monads.
The following example initializes a mutable vector, modifies it coordinate-wise, and then reads one element and the final contents:
>>>:{example1 :: (Int, [Int]) example1 = linearly \lin -> DataFlow.do (lin, lin') <- dup lin vec <- VL.fromList [0, 1, 2] lin runBO lin' Control.do (mvec, lend) <- borrowM vec mvec <- VL.modify 0 (+ 3) mvec mvec <- VL.modify 2 (+ 5) mvec mvec <- VL.modify 0 (* 4) mvec let !(Ur svec) = share mvec Ur n <- VL.copyAt 0 svec pureAfter (n, unur $ VL.toList (reclaim lend)) :}
>>>example1(12,[12,1,7])
This just returns (12, [12, 1, 7]) as expected, which is not so surprising.
But what if you want to modify non-overlapping segments of the vectors in-parallel?
In particular, while you have to do two modifications to index 0 sequentially, you can modify the segment containing original index 2 in parallel with the first modification to index 0.
This is where pure concurrency with parBO comes in:
>>>:{example2 :: (Int, [Int]) example2 = linearly \lin -> DataFlow.do (lin, lin') <- dup lin vec <- VL.fromList [0, 1, 2] lin runBO lin' Control.do (mvec, lend) <- borrowM vec let !(mvec1, mvec2) = VL.splitAt 1 mvec -- (*) (mvec, ()) <- parBO ( Control.do mvec1 <- VL.modify 0 (+ 3) mvec1 VL.modify 0 (* 4) mvec1 ) (consume Control.<$> VL.modify 1 (+ 5) mvec2) let !(Ur svec) = share mvec Ur n <- VL.copyAt 0 svec pureAfter (n, unur $ VL.toList (reclaim lend)) :}
>>>example2(12,[12,1,7])
The line after (*) splits the mutable vector into two non-overlapping mutable borrows, which can be safely used in parallel with parBO.
The left branch returns the modified first slice, while the right branch consumes its slice and returns (). The whole original vector is later recovered through lend.
Manual discarding of split resources becomes tedious quickly.
This is where the borrow-based affine API helps: reborrowing lets you work in a shorter lifetime without manually reclaiming the original borrow.
>>>:{example3 :: (Int, [Int]) example3 = linearly \lin -> DataFlow.do (lin, lin') <- dup lin vec <- VL.fromList [0, 1, 2] lin runBO lin' Control.do (mvec, lend) <- borrowM vec -- (!) mvec <- reborrowing_ mvec \mvec -> Control.do let !(mvec1, mvec2) = VL.splitAt 1 mvec -- (!!) consume Control.<$> parBO ( Control.do mvec1 <- VL.modify 0 (+ 3) mvec1 VL.modify 0 (* 4) mvec1 ) (VL.modify 1 (+ 5) mvec2) let !(Ur svec) = share mvec -- (!!!) Ur n <- VL.copyAt 0 svec pureAfter (n, unur $ VL.toList (reclaim lend)) :}
>>>example3(12,[12,1,7])
The line after (!) opens a new sublifetime with reborrowing_.
Within this sublifetime, the new mutable borrow mvec is divided into two pieces, and then both slices are modified in parallel after (!!).
This time, the split mvec1 and mvec2 are consumed after parBO returns. Once the sublifetime ends, the original mutable borrow to the whole vector is recovered and used at (!!!).
This way, you can treat and split mutable and immutable borrows freely without manually dropping or reuniting them into the original resources.
Core BO monad
data BO (α :: Lifetime) a Source #
Computation returning a that can be performed only during the lifetime α.
Internally it is a linear ST monad.
Instances
| (α >= β, a <: b) => (BO α a :: Type) <: (BO β b :: Type) Source # | |
Defined in Control.Monad.Borrow.Pure.BO.Internal | |
| Applicative (BO α) Source # | |
| Functor (BO α) Source # | |
Defined in Control.Monad.Borrow.Pure.BO.Internal | |
| Monad (BO α) Source # | |
| Applicative (BO α) Source # | |
| Functor (BO α) Source # | |
Defined in Control.Monad.Borrow.Pure.BO.Internal | |
| Monoid w => Monoid (BO α w) Source # | |
Defined in Control.Monad.Borrow.Pure.BO.Internal | |
| Semigroup w => Semigroup (BO α w) Source # | |
srunBO :: forall (β :: Lifetime) a. (forall (α :: Lifetime). BO (α /\ β) (After α a)) %1 -> BO β a Source #
Runs a BO computation within the ephemeral sublifetime and returns the result.
Lifetimes and Subtyping
Lifetime is a key concept in borrowing.
You can understand it as a version of the thread parameter s in , but refined with the subtyping relation ST s(<=) (or outlives-relation (>=)).
Every computation is parametrized with lifetime, and ordinary borrows, such as BO α or Mut α a, and lenders Share α a also have the lifetime for which they are valid.
To accommodate casting between different lifetimes, we also provide the Lend α aupcast operator that has a lifetime parameter according to the sublifetime relation.
The upcast operator casts a given type along (<:) relation, which extends (<=) to the other types appropriately.
Any two lifetimes α and β have the meet α , which is the longest lifetime that is shorter than both /\ βα and β; i.e. α is the most generic lifetime such that /\ βα and /\ β <= αα .
We use some tricks with /\ β <= β/\ to work around type-checking higher-level combinators.
For example, consider the type of srunBO_:
srunBO_:: (forall β.BO(β/\α) a) %1 ->BOα a
At first glance, the type forall β. might look rather cryptic.
But essentially, the above type is morally equivalent to the following:BO (β /\ α) a
srunBO_:: (forall β <= α.BOβ a) %1 ->BOα a
That is, all srunBO_ does is open an ephemeral sublifetime β <= α and run the computation inside it.
However, without involved hacking or type-checker plugins, the type system is not good at treating transitivity of subtyping relation.
By quantifying over all lifetimes and combining them with /\, we can make the type-checker happy without losing generality.
So, if you see a pattern that binds other lifetimes with forall and combines them with /\, you can think of it as quantifying over a sublifetime of the current lifetime.
Lifetime
type (/\) = '(:/\) infixr 3 Source #
The meet of the two lifetimes. It is the longest lifetime that is shorter than both of them.
class (α :: Lifetime) <= (β :: Lifetime) infix 2 Source #
Minimal complete definition
witness
Subtyping and upcasting
class (a :: k) <: (b :: k1) infix 4 Source #
Minimal complete definition
Instances
Linearity witnesses
When you allocate mutable resources, you must ensure that they are used only linearly; i.e. they are used exactly once.
In Linear Haskell, we use linear arrow %1 -> to express this invariant.
More precisely, a %1 -> b reads that if the application of the function is consumed exactly once, then the argument is consumed exactly once.
This definition poses a subtle problem: the resource is guaranteed to be used linearly only when the resource is bound under some linear arrow context.
Hence, we must know that we are under a linear context before allocating mutable references, otherwise the mutable state can leak outside.
The Linearly token witnesses exactly this invariant.
The important point is that it can be introduced into the context only by linearly combinator:
linearly::Movablea => (Linearly%1 -> a) %1 -> a
This assures that Linearly can be used as a linearity witness when mutable resources are allocated.
You can duplicate a Linearly token as many times as you want with dup and drop it with consume.
fromList :: [a] %1 ->Linearly%1 ->Vectora
See Linear Constraints: the Problem with Scopes for more details.
Those mutable datatypes can only be introduced via a Linearly witness, so they can be seen as carrying the Linearly witness inside.
LinearOnly is a type class for such datatypes and we can use it to recover a Linearly witness from such values.
withLinearly:: (LinearOnlya) => a %1 -> (Linearly, a)
Further, running the BO computation also requires Linearly:
runBO_ ::Linearly%1 -> (forall α.BOα a) %1 -> a
Hence, you can retrieve a Linearly token inside BO via askLinearly, asksLinearlyM, etc.
Witness that the current computation is in a linear context.
Instances
| Consumable Linearly Source # | |
| Dupable Linearly Source # | |
| LinearOnly Linearly Source # | |
Defined in Control.Monad.Borrow.Pure.Lifetime.Token.Internal Methods | |
class LinearOnly (a :: TYPE rep) Source #
A (non-bottom) value of the type a can only live in a linear context.
Minimal complete definition
Instances
| LinearOnly Linearly Source # | |
Defined in Control.Monad.Borrow.Pure.Lifetime.Token.Internal Methods | |
| LinearOnly (Now α :: Type) Source # | |
Defined in Control.Monad.Borrow.Pure.Lifetime.Token.Internal Methods linearOnly :: LinearOnlyWitness (Now α) Source # | |
| LinearOnly (Ref a :: Type) Source # | |
Defined in Data.Ref.Linear Methods linearOnly :: LinearOnlyWitness (Ref a) Source # | |
| LinearOnly (Vector a :: Type) Source # | |
Defined in Data.Vector.Mutable.Linear.Borrow Methods linearOnly :: LinearOnlyWitness (Vector a) Source # | |
| LinearOnly (Ref# a :: UnliftedType) Source # | |
Defined in Data.Ref.Linear.Unlifted Methods linearOnly :: LinearOnlyWitness (Ref# a) Source # | |
| bk ~ 'Mut => LinearOnly (Borrow bk α a :: Type) Source # | |
Defined in Control.Monad.Borrow.Pure.BO.Internal Methods linearOnly :: LinearOnlyWitness (Borrow bk α a) Source # | |
withLinearly :: LinearOnly a => a %1 -> (Linearly, a) Source #
Parallel computation
parBO :: forall (α :: Lifetime) a b. BO α a %1 -> BO α b %1 -> BO α (a, b) Source #
Run two computations in parallel, returning their results as a tuple.
Borrowing
To treat a linear resource inside BO monad, you have to borrow it first.
The most typical introduction form is borrowM:
borrowM:: a %1 ->BOα (Mutα a,Lendα a)
This borrows a linear resource into the same lifetime as the ambient BO, returning a Mutable borrow and a Lender of the original resource.
Or, you can do the linear allocation of the resource and borrow it at the same time with borrowLinearlyM:
borrowLinearlyM:: (Linearly %1 -> a) %1 ->BOα (Mutα a,Lendα a)
In any case, the main computation with possible destructive updates is done on Mutable borrows, and the original resource will be reclaimed from the Lender at the end of the lifetime α.
More precisely, must be processed in an appropriate Lend α a value that is returned to After α rrunBO, srunBO, or the reborrowing operators described later.
is a kind of finalizer that will be run after the lifetime After α aα has Ended, and it can be used to reclaim the original resource from a Lender and do further final computation such as conversion or consumption.
One can share the Mutable borrow into Shared borrow:
share::Mutα a %1 ->Ur(Shareα a)
As Share is an immutable borrow, it can be freely duplicated and dropped, as witnessed by the Ur wrapper.
Shared borrows are always introduced nonlinearly, so that you can freely use them multiple times or drop them at any time.
Note that share consumes the original Mut. If you want to share the resource temporarily into a sublifetime and then continue mutating afterwards, you can use the sharing combinator (and its variants sharing' and sharing_):
sharing:: forall α α' a r.Mutα a %1 -> (forall β.Share(β/\α) a ->BO(β/\α') r) %1 ->BOα' (r,Mutα a)
Analogously, you can reborrow mutable borrows into sublifetimes using the reborrowing combinator (and its variants reborrowing' and reborrowing_).
reborrowing:: forall α α' a r.Mutα a %1 -> (forall β.Mut(β/\α) a ->BO(β/\α') r) %1 ->BOα' (r,Mutα a)
There is an experimental interface abstracting the reborrowable borrows in Control.Monad.Borrow.Pure.Experimental.Reborrowable.
Borrow polymorphism
Mut, Share, and Lend are all specific instantiations of the Alias type:
typeMutα a =Borrow'Mut α a typeShareα a =Borrow'Share α a typeBorrowbk α a =Alias('Borrow bk) α a typeLendα a =Alias'Lend α a
Hence, if you see in a function, it can be either Borrow bk α aMut or Share. If you see , it may also be a Alias ak α aLend.
Control.Monad.Borrow.Pure.Experimental.Borrows provides an experimental API for treating a bundle of multiple borrows in the same lifetime at once.
Which combinator should I use?
- Use
borrowMto borrow an existing linear value insideBO. - Use
borrowLinearlyMto allocate a linear value and immediately borrow it insideBO. - Use
shareto permanently turn aMutinto an unrestrictedShare. - Use
sharingorsharing_to share temporarily and then regain the originalMut. - Use
reborrowingorreborrowing_to create a shorter-livedMutand then regain the originalMut. - Use
reclaimorreclaim'to recover the original resource from aLendafter the lifetime ends.
Central Borrow types
type Share = Borrow 'Share Source #
Shared borrower, which is unrestricted but usually can only read from the data.
type Borrow (bk :: BorrowKind) = Alias ('Borrow bk) Source #
Borrower of kind bk that is active during the lifetime α.
data Alias (ak :: AliasKind) (α :: Lifetime) a Source #
Alias of kind ak to a resource of type a.
Instances
| Reborrowable Mut Source # | |
| Reborrowable Share Source # | |
| (α <= β, a <: b) => (Lend α a :: Type) <: (Lend β b :: Type) Source # | |
Defined in Control.Monad.Borrow.Pure.BO.Internal | |
| (α >= β, a <: b, b <: a) => (Mut α a :: Type) <: (Mut β b :: Type) Source # | |
Defined in Control.Monad.Borrow.Pure.BO.Internal | |
| (α >= β, a <: b) => (Share α a :: Type) <: (Share β b :: Type) Source # | |
Defined in Control.Monad.Borrow.Pure.BO.Internal | |
| bk ~ 'Mut => LinearOnly (Borrow bk α a :: Type) Source # | |
Defined in Control.Monad.Borrow.Pure.BO.Internal Methods linearOnly :: LinearOnlyWitness (Borrow bk α a) Source # | |
| Consumable (Borrow bk α a) Source # | |
Defined in Control.Monad.Borrow.Pure.BO.Internal | |
| k ~ 'Borrow 'Share => Dupable (Alias k α a) Source # | |
| k ~ 'Borrow 'Share => Movable (Alias k α a) Source # | |
| Affine (Borrow bk α a) Source # | |
Introduction form
borrowM :: forall a (α :: Lifetime). a %1 -> BO α (Mut α a, Lend α a) Source #
Borrow a resource linearly for the same lifetime as the ambient BO computation.
Returns the pair of the mutable borrow to the resource, and Lender to be invoked later to reclaim the resource at the End of the lifetime.
See also borrowLinearlyM.
If you want to borrow a resource indepdendent of the ambient lifetime, you can use borrow instead.
borrowLinearlyM :: forall a (α :: Lifetime). (Linearly %1 -> a) %1 -> BO α (Mut α a, Lend α a) Source #
A variant of borrowM that does linear allocation first.
share :: forall (k :: BorrowKind) (α :: Lifetime) a. Borrow k α a %1 -> Ur (Share α a) Source #
Shares a mutable borrow, invalidating the original one.
Reborrowing and computation in sublifetime
reborrowing' :: forall (α :: Lifetime) a (α' :: Lifetime) r. Mut α a %1 -> (forall (β :: Lifetime). Mut (β /\ α) a %1 -> BO (β /\ α') (After β r)) %1 -> BO α' (r, Mut α a) Source #
Executes an operation on Mutable borrow in sub lifetime.
You may need -XImpredicativeTypes extension to use this function.
See also: reborrowing, and reborrowing_. For Shared borrows, see sharing, sharing', and sharing_.
reborrowing :: forall (α :: Lifetime) a (α' :: Lifetime) r. Mut α a %1 -> (forall (β :: Lifetime). Mut (β /\ α) a %1 -> BO (β /\ α') r) %1 -> BO α' (r, Mut α a) Source #
A variant of reborrowing' that returns the direct value of the operation on the reborrowed mutable borrow.
There is also a flipped infix version (<%~).
See also: reborrowing_ and sharing.
(<%~) :: forall (α :: Lifetime) a (α' :: Lifetime) r. (forall (β :: Lifetime). Mut (β /\ α) a %1 -> BO (β /\ α') r) %1 -> Mut α a %1 -> BO α' (r, Mut α a) infix 4 Source #
Flipped infix version of reborrowing, smoewhat analgous to (<$>) and (<%~) in lens package.
reborrowing_ :: forall r (α :: Lifetime) a (α' :: Lifetime). Consumable r => Mut α a %1 -> (forall (β :: Lifetime). Mut (β /\ α) a %1 -> BO (β /\ α') r) %1 -> BO α' (Mut α a) Source #
A variant of reborrowing' that discards the final result of the computation.
There is also a flipped infix version (<%=).
See also: reborrowing and sharing_.
(<%=) :: forall (α :: Lifetime) a (α' :: Lifetime). (forall (β :: Lifetime). Mut (β /\ α) a %1 -> BO (β /\ α') ()) %1 -> Mut α a %1 -> BO α' (Mut α a) infix 4 Source #
Flipped infix version of reborrowing_, smoewhat analgous to (<$>) and (<%=) in lens package.
sharing' :: forall (α :: Lifetime) a (α' :: Lifetime) r. Mut α a %1 -> (forall (β :: Lifetime). Share (β /\ α) a -> BO (β /\ α') (After β r)) %1 -> BO α' (r, Mut α a) Source #
Executes an operation on Shared borrow in sub lifetime.
You may need -XImpredicativeTypes extension to use this function.
See also: sharing and sharing_. For Mutable borrows, see reborrowing'.
sharing :: forall (α :: Lifetime) (α' :: Lifetime) a r. Mut α a %1 -> (forall (β :: Lifetime). Share (β /\ α) a -> BO (β /\ α') r) %1 -> BO α' (r, Mut α a) Source #
A variant of sharing' that returns the direct value of the computation on the shared borrow.
There is also a flipped infix version (<$~).
See also: sharing_. For Mutable borrows, see reborrowing.
(<$~) :: forall (α :: Lifetime) a (α' :: Lifetime) r. (forall (β :: Lifetime). Share (β /\ α) a -> BO (β /\ α') r) %1 -> Mut α a %1 -> BO α' (r, Mut α a) infix 4 Source #
sharing_ :: forall (α :: Lifetime) (α' :: Lifetime) a r. Consumable r => Mut α a %1 -> (forall (β :: Lifetime). Share (β /\ α) a -> BO (β /\ α') r) %1 -> BO α' (Mut α a) Source #
A variant of sharing' that discards the final result of the computation.
There is also a flipped infix version (<$=).
See also: sharing. For Mutable borrows, see reborrowing_.
(<$=) :: forall (α :: Lifetime) a (α' :: Lifetime). (forall (β :: Lifetime). Share (β /\ α) a -> BO (β /\ α') ()) %1 -> Mut α a %1 -> BO α' (Mut α a) Source #
Finalization and reclamation
newtype After (α :: Lifetime) a Source #
Utility type to represent an object available after the lifetime α.
You can use Applicative and Monad instances to write After conveniently.
Instances
| (α <= β, a <: b) => (After α a :: Type) <: (After β b :: Type) Source # | |
| Applicative (After α) Source # | |
| Functor (After α) Source # | |
| Monad (After α) Source # | |
| Applicative (After α) Source # | |
| Functor (After α) Source # | |
In-place modification with mutable borrows
modifyBO :: a %1 -> Linearly %1 -> (forall (α :: Lifetime). Mut α a %1 -> BO α r) %1 -> (r, a) Source #
Modifies linear resources in-place, together with results.
modifyBO_ :: a %1 -> Linearly %1 -> (forall (α :: Lifetime). Mut α a %1 -> BO α ()) %1 -> a Source #
Modifies linear resources in-place, without results.
modifyLinearOnlyBO :: LinearOnly a => a %1 -> (forall (α :: Lifetime). Mut α a %1 -> BO α r) %1 -> (r, a) Source #
Modifies linear resources in-place, together with results.
modifyLinearOnlyBO_ :: LinearOnly a => a %1 -> (forall (α :: Lifetime). Mut α a %1 -> BO α ()) %1 -> a Source #
Modifies linear resources in-place, together with results.
Utility function to manipulate borrows
joinMut :: forall (bk :: BorrowKind) (α :: Lifetime) (β :: Lifetime) a. Borrow bk α (Mut β a) %1 -> Borrow bk (α /\ β) a Source #
Collapse a borrower to a mutable borrower.
Copying and Cloning
For some types, you can copy them as the direct value out of a borrow:
copy::Borrowbk α a %1 -> a
Note that copy consumes a borrow linearly.
For Shared borrows it doesn't matter because they are always introduced nonlinearly.
But for Mutable borrows, we cannot use a copyed value multiple times as Muts are always bound linearly.
To alleviate this problem, we also provide copyMut that wraps copied value inside Ur:
copyMut::Mutα a %1 ->Ura
Precisely, if the type a does not contain any mutable or foreign resources, it can be safely Copyable out of borrows.
Some examples are (but not limited to):
- Primitive types, such as
Int,Bool, etc. - Immutable data structures, such as lists, tuples of them, etc. (but not mutable vectors, arrays, etc.)
For possibly mutable types, you can still clone them out of borrows linearly inside BO-monad:
clone::Clonea =>Shareα a %1 ->BOα a
This includes, for example, Ref or Vector.
The fact that the cloned value is only accessible inside BO ensures that we cannot leak mutable states inside a into unrestricted contexts -- otherwise, we can introduce mutable values into unrestricted context via .move :: Share α a -> Ur (Share α a)
class Copyable a where Source #
Instances
is analogous o Clone a, but requires cloned values
to be accessible only inside the Copyable a monad.BO α
The difference between Clone and Copyable is that the former allows for
cloning a shared borrow of a mutable or linear value, while the latter requires cloning a shared borrow of an immutable value.
This is because we can leak via Share α aMovable instance, and
hence it can outlive the original lifetime, which allows leaking mutable states inside BO αa into unrestricted contexts, which destroys the soundness severly.
Minimal complete definition
Nothing
Instances
Splitting aliases
You can do case-splitting on Borrows - for example:
splitPair::Aliasak α (a, b) %1 -> (Aliasak α a,Aliasak α b)splitEither::Aliasak α (Eithera b) %1 ->Either(Aliasak α a) (Aliasak α b)
For other datatypes, you can use split to split general parametric types into borrows.
It is morally an instance method of the DistributesAlias class, and you can derive it using anyclass derivation together with the deriveGenericAnd1 macro.
We also provide experimental splitting on record types in Data.Record.Linear.Borrow.Experimental.PatternMatch and Data.Record.Linear.Borrow.Experimental.Split.
splitPair :: forall (ak :: AliasKind) (α :: Lifetime) a b. Alias ak α (a, b) %1 -> (Alias ak α a, Alias ak α b) Source #
splitEither :: forall (ak :: AliasKind) (α :: Lifetime) a b. Alias ak α (Either a b) %1 -> Either (Alias ak α a) (Alias ak α b) Source #
split :: forall f x (ak :: AliasKind) (α :: Lifetime). DistributesAlias f => Alias ak α (f x) %1 -> f (Alias ak α x) Source #
class DistributesAlias (f :: Type -> Type) Source #
Distribute an alias over a functor.
Instances
| DistributesAlias First Source # | |
| DistributesAlias Last Source # | |
| DistributesAlias Max Source # | |
| DistributesAlias Min Source # | |
| DistributesAlias Identity Source # | |
| DistributesAlias First Source # | |
| DistributesAlias Last Source # | |
| DistributesAlias Down Source # | |
| DistributesAlias Dual Source # | |
| DistributesAlias Par1 Source # | |
| DistributesAlias Maybe Source # | |
| DistributesAlias Solo Source # | |
| DistributesAlias [] Source # | |
| Unsatisfiable ('Text "Use splitEither directly!") => DistributesAlias (Either e) Source # | |
| Unsatisfiable ('Text "Use splitPair instead!") => DistributesAlias ((,) a) Source # | |
| GenericDistributesAlias f => DistributesAlias (Generically1 f) Source # | |
Defined in Control.Monad.Borrow.Pure.BO.Internal Methods split_ :: forall (ak :: AliasKind) (α :: Lifetime) x. Alias ak α (Generically1 f x) %1 -> Generically1 f (Alias ak α x) | |
genericSplit :: forall f x (ak :: AliasKind) (α :: Lifetime). GenericDistributesAlias f => Alias ak α (f x) %1 -> f (Alias ak α x) Source #
Re-exporting Prelude.Linear classes
class Consumable a where #
Instances
class Consumable a => Dupable a where #
The laws of Dupable are dual to those of Monoid:
- 1.
first consume (dup2 a) ≃ a ≃ second consume (dup2 a)(dup2neutrality) - 2.
first dup2 (dup2 a) ≃ (second dup2 (dup2 a))(dup2associativity)
where the (≃) sign represents equality up to type isomorphism.
- 3.
dup2 = Replicator.elim (,) . dupR(coherence betweendup2anddupR) - 4.
consume = Replicator.elim () . dupR(coherence betweenconsumeanddupR) - 5.
Replicator.extract . dupR = id(dupRidentity) - 6.
dupR . dupR = (Replicator.map dupR) . dupR(dupRinterchange)
(Laws 1-2 and 5-6 are equivalent)
Implementation of Dupable for Movable types should
be done with deriving via .AsMovable
Implementation of Dupable for other types can be done with
deriving via . Note that at present this mechanism
can have performance problems for recursive parameterized types.
Specifically, the methods will not specialize to underlying GenericallyDupable
instances. See this GHC issue.
Methods
dupR :: a %1 -> Replicator a #
Creates a Replicator for the given a.
You usually want to define this method using Replicator's
Applicative instance. For instance, here is an
implementation of :Dupable [a]
instance Dupable a => Dupable [a] where dupR [] = pure [] dupR (a : as) = (:) <$> dupR a <*> dupR as
Creates two as from a , in a linear fashion.Dupable a
Instances
class Dupable a => Movable a where #
Use to represent a type which can be used many times even
when given linearly. Simple data types such as Movable aBool or [] are Movable.
Though, bear in mind that this typically induces a deep copy of the value.
Formally, is the class of
coalgebras of the
Movable aUr comonad. That is
unur (move x) = x
move @(Ur a) (move @a x) = fmap (move @a) $ move @a x
Additionally, a Movable instance must be compatible with its Dupable parent instance. That is:
case move x of {Ur _ -> ()} = consume xcase move x of {Ur x -> (x, x)} = dup2 x
Instances
Ur a represents unrestricted values of type a in a linear
context. The key idea is that because the contructor holds a with a
regular arrow, a function that uses Ur a linearly can use a
however it likes.
someLinear :: Ur a %1-> (a,a) someLinear (Ur a) = (a,a)