linear-base 0.6.0 → 0.7.0
raw patch · 12 files changed
+308/−5 lines, 12 filesdep +bytestringdep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: bytestring
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- CHANGELOG.md +11/−0
- linear-base.cabal +3/−1
- src/Control/Functor/Linear/Internal/Reader.hs +12/−0
- src/Data/Functor/Linear/Internal/Functor.hs +6/−0
- src/Data/Functor/Linear/Internal/Traversable.hs +7/−0
- src/Data/HashMap/Mutable/Linear/Internal.hs +6/−2
- src/Data/List/NonEmpty/Linear.hs +211/−0
- src/Data/Monoid/Linear/Internal/Semigroup.hs +9/−0
- src/Data/Ord/Linear/Internal/Eq.hs +4/−0
- src/Data/Ord/Linear/Internal/Ord.hs +5/−0
- src/Data/Set/Mutable/Linear/Internal.hs +10/−2
- src/Data/Unrestricted/Linear/Internal/Instances.hs +24/−0
CHANGELOG.md view
@@ -1,5 +1,16 @@ # Change Log +## [v0.7.0](https://github.com/tweag/linear-base/tree/v0.7.0) (2026-02-27)++[Full Changelog](https://github.com/tweag/linear-base/compare/v0.6.0...v0.7.0)++### Headline changes++- Add instance Consumable/Dupable/Movable for ByteString, ShortByteString and Text [\#501](https://github.com/tweag/linear-base/pull/501) ([Bodigrim](https://github.com/Bodigrim))+- feat: Adds missing instances for `NonEmpty` and adds some Linear counterpart of `Data.List.NonEmpty` [\#495](https://github.com/tweag/linear-base/pull/495) ([konn](https://github.com/konn))+- use Unsatisfiable for banned instances \(unrestricted Monoid / Semigroup for linear containers\) [\#494](https://github.com/tweag/linear-base/pull/494) ([konn](https://github.com/konn))+- Control Monad instance for linear arrows [\#493](https://github.com/tweag/linear-base/pull/493) ([konn](https://github.com/konn))+ ## [v0.6.0](https://github.com/tweag/linear-base/tree/v0.6.0) (2025-11-18) [Full Changelog](https://github.com/tweag/linear-base/compare/v0.5.0...v0.6.0)
linear-base.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: linear-base-version: 0.6.0+version: 0.7.0 license: MIT license-file: LICENSE copyright: (c) Tweag Holding and affiliates@@ -79,6 +79,7 @@ Data.HashMap.Mutable.Linear Data.HashMap.Mutable.Linear.Internal Data.List.Linear+ Data.List.NonEmpty.Linear Data.Maybe.Linear Data.Monoid.Linear Data.Monoid.Linear.Internal.Monoid@@ -134,6 +135,7 @@ default-language: Haskell2010 build-depends: base >=4.16 && <5,+ bytestring, containers, ghc-prim, ghc-bignum,
src/Control/Functor/Linear/Internal/Reader.hs view
@@ -1,5 +1,9 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleInstances #-} {-# OPTIONS -Wno-orphans #-} {-# LANGUAGE LinearTypes #-}+{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE NoImplicitPrelude #-} {-# OPTIONS_HADDOCK hide #-} @@ -28,8 +32,10 @@ import Data.Functor.Identity import qualified Data.Functor.Linear.Internal.Applicative as Data import qualified Data.Functor.Linear.Internal.Functor as Data+import Data.Kind (FUN) import Data.Unrestricted.Linear.Internal.Consumable import Data.Unrestricted.Linear.Internal.Dupable+import GHC.Exts (Multiplicity (..)) import Prelude.Linear.Internal (runIdentity', ($), (.)) -- # Linear ReaderT@@ -119,3 +125,9 @@ instance MonadTrans (NonLinear.ReaderT r) where lift x = NonLinear.ReaderT (\_ -> x)++deriving via Reader r instance (Dupable r) => Data.Applicative (FUN 'One r)++deriving via Reader r instance (Dupable r) => Applicative (FUN 'One r)++deriving via Reader r instance (Dupable r) => Monad (FUN 'One r)
src/Data/Functor/Linear/Internal/Functor.hs view
@@ -32,6 +32,7 @@ import Data.Functor.Product import Data.Functor.Sum import Data.Kind (FUN)+import Data.List.NonEmpty (NonEmpty) import Data.Unrestricted.Linear.Internal.Consumable import Data.Unrestricted.Linear.Internal.Ur import GHC.Types (Multiplicity (..))@@ -75,6 +76,11 @@ go :: [a] %1 -> [b] go [] = [] go (a : as) = f a : go as++deriving via+ Generically1 NonEmpty+ instance+ Functor NonEmpty deriving via Generically1 (Const x)
src/Data/Functor/Linear/Internal/Traversable.hs view
@@ -34,6 +34,7 @@ import Data.Functor.Const import qualified Data.Functor.Linear.Internal.Applicative as Data import qualified Data.Functor.Linear.Internal.Functor as Data+import Data.List.NonEmpty (NonEmpty (..)) import GHC.Types (Multiplicity (..)) import Generics.Linear import Prelude.Linear.Internal@@ -130,6 +131,12 @@ where go [] = Control.pure [] go (x : xs) = Control.liftA2 (:) (f x) (go xs)++instance Traversable NonEmpty where+ -- We define traverse explicitly both to allow specialization+ -- to the appropriate Applicative and to allow specialization to+ -- the passed function. The generic definition allows neither, sadly.+ traverse f (x :| xs) = (:|) Control.<$> f x Control.<*> traverse f xs instance Traversable ((,) a) where traverse = genericTraverse
src/Data/HashMap/Mutable/Linear/Internal.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE InstanceSigs #-}@@ -25,7 +27,9 @@ import Data.Hashable import qualified Data.Maybe as NonLinear import Data.Unrestricted.Linear+import GHC.TypeLits (ErrorMessage (..)) import Prelude.Linear hiding (filter, insert, lookup, mapMaybe, read, (+))+import Prelude.Linear.Unsatisfiable (Unsatisfiable, unsatisfiable) import Unsafe.Coerce (unsafeCoerce) import qualified Unsafe.Linear as Unsafe import Prelude ((+))@@ -481,8 +485,8 @@ ) arr -instance Prelude.Semigroup (HashMap k v) where- (<>) = error "Prelude.<>: invariant violation, unrestricted HashMap"+instance (Unsatisfiable ('Text "Using Prelude's Semigroup methods on a Data.HashMap.Mutable.Linear is vacuous as there can't be an unrestricted such Hashmap")) => Prelude.Semigroup (HashMap k v) where+ (<>) = unsatisfiable instance (Keyed k) => Semigroup (HashMap k v) where (<>) = union
+ src/Data/List/NonEmpty/Linear.hs view
@@ -0,0 +1,211 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE LinearTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# OPTIONS_GHC -Wno-name-shadowing #-}+{-# OPTIONS_GHC -Wno-orphans #-}++-- |+-- Linear versions of 'NonEmpty' functions.+--+-- This module only contains minimal amount of documentation; consult the+-- original "Data.List.NonEmpty" module for more detailed information.+module Data.List.NonEmpty.Linear+ ( -- * Non-empty stream transformations+ NonEmpty (..),+ map,+ intersperse,+ scanl,+ scanr,+ scanl1,+ scanr1,+ transpose,+ NonLinear.sortBy,+ NonLinear.sortWith,++ -- * Basic functions+ length,+ NonLinear.head,+ NonLinear.tail,+ NonLinear.last,+ NonLinear.init,+ singleton,+ (<|),+ cons,+ uncons,+ unfoldr,+ NonLinear.sort,+ reverse,+ append,+ appendList,+ prependList,++ -- * Extracting sublists+ take,+ drop,+ splitAt,+ takeWhile,+ dropWhile,+ span,+ break,+ filter,+ partition,++ -- * Zipping and unzipping streams+ zip,+ zipWith,+ zip',+ zipWith',+ unzip,+ unzip3,++ -- * Converting to and from a list+ fromList,+ toList,+ nonEmpty,+ xor,+ )+where++import qualified Data.List.Linear as List+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonLinear+import Data.Vector.Internal.Check (HasCallStack)+import Prelude.Linear hiding (drop, dropWhile, filter, intersperse, length, map, partition, reverse, scanl, scanl1, scanr, scanr1, span, splitAt, take, takeWhile, transpose, uncons, unfoldr, unzip, unzip3, zip, zip', zipWith, zipWith')+import qualified Unsafe.Linear as Unsafe+import qualified Prelude as Prelude++map :: (a %1 -> b) -> NonEmpty a %1 -> NonEmpty b+map f (x :| xs) = f x :| List.map f xs++intersperse :: a -> NonEmpty a %1 -> NonEmpty a+intersperse a = Unsafe.toLinear (NonLinear.intersperse a)++reverse :: NonEmpty a %1 -> NonEmpty a+reverse = Unsafe.toLinear NonLinear.reverse++scanl :: (Dupable b) => (b %1 -> a %1 -> b) -> b %1 -> NonEmpty a %1 -> NonEmpty b+scanl f z = fromList . List.scanl f z . toList++scanr :: (Dupable b) => (a %1 -> b %1 -> b) -> b %1 -> NonEmpty a %1 -> NonEmpty b+scanr f z = fromList . List.scanr f z . toList++scanl1 :: (Dupable a) => (a %1 -> a %1 -> a) -> NonEmpty a %1 -> NonEmpty a+scanl1 f (x :| xs) = fromList $ List.scanl f x xs++scanr1 :: (Dupable a) => (a %1 -> a %1 -> a) -> NonEmpty a %1 -> NonEmpty a+scanr1 f (x :| xs) = fromList $ List.scanr1 f (x : xs)++transpose :: NonEmpty (NonEmpty a) %1 -> NonEmpty (NonEmpty a)+transpose = Unsafe.toLinear NonLinear.transpose++singleton :: a %1 -> NonEmpty a+singleton = (:| [])++infixr 5 <|++(<|) :: a %1 -> NonEmpty a %1 -> NonEmpty a+a <| bs = a :| toList bs++cons :: a %1 -> NonEmpty a %1 -> NonEmpty a+cons = (<|)++uncons :: NonEmpty a %1 -> (a, Maybe (NonEmpty a))+uncons (x :| xs) = (x, nonEmpty xs)++unfoldr :: (a %1 -> (b, Maybe a)) -> a %1 -> NonEmpty b+unfoldr f a = case f a of+ (b, mc) -> b :| maybe [] go mc+ where+ go c = case f c of+ (d, me) -> d : maybe [] go me++append :: NonEmpty a %1 -> NonEmpty a %1 -> NonEmpty a+append = (<>)++appendList :: NonEmpty a %1 -> [a] %1 -> NonEmpty a+appendList (x :| xs) ys = x :| (xs <> ys)++prependList :: [a] %1 -> NonEmpty a %1 -> NonEmpty a+prependList ls ne = case ls of+ [] -> ne+ (y : ys) -> y :| (ys <> toList ne)++-- | __NOTE__: This does not short-circuit and always traverses the+-- entire list to consume the rest of the elements.+take :: (Consumable a) => Int -> NonEmpty a %1 -> [a]+take n = List.take n . toList++drop :: (Consumable a) => Int -> NonEmpty a %1 -> [a]+drop n = List.drop n . toList++splitAt :: (Consumable a) => Int -> NonEmpty a %1 -> ([a], [a])+splitAt n = List.splitAt n . toList++-- | __NOTE__: This does not short-circuit and always traverses the+-- entire list to consume the rest of the elements.+takeWhile :: (Dupable a) => (a %1 -> Bool) -> NonEmpty a %1 -> [a]+takeWhile p = List.takeWhile p . toList++dropWhile :: (Dupable a) => (a %1 -> Bool) -> NonEmpty a %1 -> [a]+dropWhile p = List.dropWhile p . toList++span :: (Dupable a) => (a %1 -> Bool) -> NonEmpty a %1 -> ([a], [a])+span p = List.span p . toList++break :: (Dupable a) => (a %1 -> Bool) -> NonEmpty a %1 -> ([a], [a])+break p = span (not . p)++filter :: (Dupable a) => (a %1 -> Bool) -> NonEmpty a %1 -> [a]+filter p = List.filter p . toList++partition :: (Dupable a) => (a %1 -> Bool) -> NonEmpty a %1 -> ([a], [a])+partition p = List.partition p . toList++-- | Return the length of the given list alongside with the list itself.+length :: NonEmpty a %1 -> (Ur Int, NonEmpty a)+length = Unsafe.toLinear $ \xs ->+ (Ur (NonLinear.length xs), xs)++fromList :: (HasCallStack) => [a] %1 -> (NonEmpty a)+fromList (x : xs) = x :| xs+fromList [] = Prelude.error "NonEmpty.Linear.fromList: empty list"++toList :: NonEmpty a %1 -> [a]+toList (x :| xs) = x : xs++nonEmpty :: [a] %1 -> Maybe (NonEmpty a)+nonEmpty (x : xs) = Just (x :| xs)+nonEmpty [] = Nothing++xor :: NonEmpty Bool %1 -> Bool+xor = Unsafe.toLinear NonLinear.xor++zip :: (Consumable a, Consumable b) => NonEmpty a %1 -> NonEmpty b %1 -> NonEmpty (a, b)+zip = zipWith (,)++zipWith :: (Consumable a, Consumable b) => (a %1 -> b %1 -> c) -> NonEmpty a %1 -> NonEmpty b %1 -> NonEmpty c+zipWith f (a :| as) (b :| bs) = f a b :| List.zipWith f as bs++-- | Same as 'zipWith', but returns the leftovers instead of consuming them.+-- Because the leftovers are returned at toplevel, @zipWith'@ is pretty strict:+-- forcing the second cons cell of the returned list forces all the recursive+-- calls.+zipWith' :: (a %1 -> b %1 -> c) -> NonEmpty a %1 -> NonEmpty b %1 -> (NonEmpty c, Maybe (Either (NonEmpty a) (NonEmpty b)))+zipWith' f (a :| as) (b :| bs) =+ case List.zipWith' f as bs of+ (cs, may) -> (f a b :| cs, may)++-- | Same as 'zip', but returns the leftovers instead of consuming them.+zip' :: NonEmpty a %1 -> NonEmpty b %1 -> (NonEmpty (a, b), Maybe (Either (NonEmpty a) (NonEmpty b)))+zip' = zipWith' (,)++unzip :: NonEmpty (a, b) %1 -> (NonEmpty a, NonEmpty b)+unzip ((a, b) :| asbs) =+ List.unzip asbs & \(as, bs) ->+ (a :| as, b :| bs)++unzip3 :: NonEmpty (a, b, c) %1 -> (NonEmpty a, NonEmpty b, NonEmpty c)+unzip3 ((a, b, c) :| abs) =+ List.unzip3 abs & \(as, bs, cs) ->+ (a :| as, b :| bs, c :| cs)
src/Data/Monoid/Linear/Internal/Semigroup.hs view
@@ -11,6 +11,7 @@ module Data.Monoid.Linear.Internal.Semigroup ( -- * Semigroup Semigroup (..),+ sconcat, -- * Endo Endo (..),@@ -34,6 +35,7 @@ import Data.Functor.Const (Const (..)) import Data.Functor.Identity (Identity (..)) import qualified Data.Functor.Product as Functor+import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.Monoid as Monoid import Data.Ord (Down (..)) import Data.Proxy (Proxy (..))@@ -64,6 +66,13 @@ class Semigroup a where (<>) :: a %1 -> a %1 -> a infixr 6 <> -- same fixity as base.<>++sconcat :: (Semigroup a) => NonEmpty a %1 -> a+sconcat (x :| xs :: NonEmpty a) = go x xs+ where+ go :: a %1 -> [a] %1 -> a+ go acc [] = acc+ go acc (x' : xs') = go (acc <> x') xs' -- | An @'Endo' a@ is just a linear function of type @a %1-> a@. -- This has a classic monoid definition with 'id' and '(.)'.
src/Data/Ord/Linear/Internal/Eq.hs view
@@ -13,6 +13,7 @@ import Data.Bool.Linear import Data.Int (Int16, Int32, Int64, Int8)+import Data.List.NonEmpty (NonEmpty (..)) import Data.Unrestricted.Linear import Data.Word (Word16, Word32, Word64, Word8) import Prelude.Linear.Internal@@ -46,6 +47,9 @@ [] == [] = True (x : xs) == (y : ys) = x == y && xs == ys xs == ys = (xs, ys) `lseq` False++instance (Consumable a, Eq a) => Eq (NonEmpty a) where+ (x :| xs) == (y :| ys) = x == y && xs == ys instance (Consumable a, Eq a) => Eq (Prelude.Maybe a) where Prelude.Nothing == Prelude.Nothing = True
src/Data/Ord/Linear/Internal/Ord.hs view
@@ -15,6 +15,7 @@ import Data.Bool.Linear (Bool (..), not) import Data.Int (Int16, Int32, Int64, Int8)+import Data.List.NonEmpty (NonEmpty (..)) import Data.Monoid.Linear import Data.Ord (Ordering (..)) import Data.Ord.Linear.Internal.Eq@@ -118,6 +119,10 @@ case compare x y of EQ -> compare xs ys res -> (xs, ys) `lseq` res++instance (Consumable a, Ord a) => Ord (NonEmpty a) where+ compare (x :| xs) (y :| ys) =+ compare x y <> compare xs ys instance (Ord a, Ord b) => Ord (a, b) where (a, b) `compare` (a', b') =
src/Data/Set/Mutable/Linear/Internal.hs view
@@ -1,10 +1,13 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LinearTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StrictData #-} {-# LANGUAGE TupleSections #-}+{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE NoImplicitPrelude #-} {-# OPTIONS_GHC -Wno-name-shadowing #-} {-# OPTIONS_HADDOCK hide #-}@@ -14,7 +17,9 @@ import qualified Data.HashMap.Mutable.Linear as Linear import Data.Monoid.Linear import Data.Unrestricted.Linear+import GHC.TypeLits (ErrorMessage (..)) import qualified Prelude.Linear as Linear hiding (insert)+import Prelude.Linear.Unsatisfiable (Unsatisfiable, unsatisfiable) import Prelude (Bool, Int) import qualified Prelude @@ -70,8 +75,11 @@ -- # Typeclass Instances ------------------------------------------------------------------------------- -instance Prelude.Semigroup (Set a) where- (<>) = Prelude.error "Prelude.(<>): invariant violation, unrestricted Set"+instance+ (Unsatisfiable ('Text "Using Prelude's Semigroup methods on a Data.Set.Mutable.Linear is vacuous as there can't be an unrestricted such Set")) =>+ Prelude.Semigroup (Set a)+ where+ (<>) = unsatisfiable instance (Keyed a) => Semigroup (Set a) where (<>) = union
src/Data/Unrestricted/Linear/Internal/Instances.hs view
@@ -21,10 +21,13 @@ -- other classes (for cleanness) in this module to avoid this dependence. module Data.Unrestricted.Linear.Internal.Instances where +import Data.ByteString (ByteString)+import Data.ByteString.Short (ShortByteString) import qualified Data.Functor.Linear.Internal.Applicative as Data import qualified Data.Functor.Linear.Internal.Functor as Data import Data.Monoid.Linear import Data.Replicator.Linear.Internal.Instances ()+import qualified Data.Text import Data.Unrestricted.Linear.Internal.Consumable import Data.Unrestricted.Linear.Internal.Dupable import Data.Unrestricted.Linear.Internal.Movable@@ -189,6 +192,27 @@ V . Unsafe.toLinear (Vector.fromListN (V.theLength @n)) Data.<$> dupR (Unsafe.toLinear Vector.toList xs)++instance Movable ByteString where+ move = Unsafe.toLinear $ \s -> s `seq` Ur s++deriving via (AsMovable ByteString) instance Consumable ByteString++deriving via (AsMovable ByteString) instance Dupable ByteString++instance Movable ShortByteString where+ move = Unsafe.toLinear $ \s -> s `seq` Ur s++deriving via (AsMovable ShortByteString) instance Consumable ShortByteString++deriving via (AsMovable ShortByteString) instance Dupable ShortByteString++instance Movable Data.Text.Text where+ move = Unsafe.toLinear $ \s -> s `seq` Ur s++deriving via (AsMovable Data.Text.Text) instance Consumable Data.Text.Text++deriving via (AsMovable Data.Text.Text) instance Dupable Data.Text.Text -- Some stock instances