unpacked-either (empty) → 0.1.0.0
raw patch · 5 files changed
+746/−0 lines, 5 filesdep +QuickCheckdep +basedep +ghc-primsetup-changed
Dependencies added: QuickCheck, base, ghc-prim, quickcheck-classes, unpacked-either
Files
- LICENSE +28/−0
- Setup.hs +2/−0
- src/Data/Either/Unpacked.hs +590/−0
- test/Main.hs +74/−0
- unpacked-either.cabal +52/−0
+ LICENSE view
@@ -0,0 +1,28 @@+Copyright © 2018 chessai++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of chessai nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Data/Either/Unpacked.hs view
@@ -0,0 +1,590 @@+--------------------------------------------------------------------------------++-- Copyright © 2018 chessai++-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions are met:+-- +-- * Redistributions of source code must retain the above copyright+-- notice, this list of conditions and the following disclaimer.+-- +-- * Redistributions in binary form must reproduce the above+-- copyright notice, this list of conditions and the following+-- disclaimer in the documentation and/or other materials provided+-- with the distribution.+-- +-- * Neither the name of chessai nor the names of other+-- contributors may be used to endorse or promote products derived+-- from this software without specific prior written permission.+-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+-- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.++--------------------------------------------------------------------------------++{-# OPTIONS_GHC -Wall #-}++--------------------------------------------------------------------------------++{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE UnboxedSums #-}+{-# LANGUAGE UnboxedTuples #-}++--------------------------------------------------------------------------------++{-| This module is intended to be a drop-in replacement+ for base's /Data.Either/. To shave off pointer chasing, it+ uses @'-XUnboxedSums'@ to represent the @'Either'@ type+ as two machine words that are contiguous in memory, without+ loss of expressiveness that /Data.Either/ provides.++ This library provides pattern synonyms @'Left'@ and @'Right'@+ that allow users to pattern match on an unpacked Either+ in a familiar way.++ Functions are also provided for converting an unpacked Either+ to the base library's Either, and vice versa.++ This library is in alpha, and the internals+ are likely to change.+-}++module Data.Either.Unpacked+ ( Either(Either, Left, Right)+ , left+ , right+ , either+ , isLeft+ , isRight+ , lefts+ , rights+ , partitionEithers+ , fromLeft+ , fromRight+ , fromBaseEither+ , toBaseEither+ ) where++--------------------------------------------------------------------------------++import Prelude+ ()++import Control.Applicative (Alternative((<|>)), Applicative((<*>), pure, liftA2))++import Control.Monad (Monad(return, (>>=)))++import Data.Bifoldable (Bifoldable(bifoldMap))+import Data.Bifunctor (Bifunctor(bimap))+import Data.Bitraversable (Bitraversable(bitraverse))++import Data.Eq (Eq((==)))+import Data.Foldable+ (Foldable(foldMap, foldr, foldl, length, null, product, sum), foldr')++import Data.Function (const, id, flip, (.), ($))+import Data.Functor (Functor(fmap))+import Data.Functor.Classes+ ( Eq1(liftEq)+ , Ord1(liftCompare)+ , Read1(liftReadPrec, liftReadListPrec, liftReadList)+ , Show1(liftShowsPrec)+ , readData+ , readUnaryWith+ , liftReadListPrecDefault+ , liftReadListDefault+ , showsUnaryWith+ + , Eq2(liftEq2)+ , Ord2(liftCompare2)+ , Read2(liftReadPrec2, liftReadListPrec2, liftReadList2)+ , Show2(liftShowsPrec2) + , liftReadList2Default + , liftReadListPrec2Default + )++import qualified Data.Either as BaseEither+import Data.Monoid (Monoid(mempty,mappend))+import Data.Ord (Ord(compare, (>=)), Ordering(GT, LT))+import Data.Semigroup (Semigroup((<>)))+import Data.Traversable (Traversable(sequenceA, traverse))++import GHC.Base (Bool(False,True))+import GHC.Read (Read(readPrec), expectP)+import GHC.Show (Show(showsPrec, showList), showString, showParen, showList__)++import Text.Read (parens, Lexeme(Ident), (+++), readListPrec, readListDefault, readListPrecDefault)+import qualified Text.Read as TextRead+import Text.ParserCombinators.ReadPrec+ (prec, step)++--------------------------------------------------------------------------------++{-|++The 'Either' type represents values with two possibilities: a value of+type @'Either' a b@ is either @'Left' a@ or @'Right' b@.++The 'Either' type is sometimes used to represent a value which is+either correct or an error; by convention, the 'Left' constructor is+used to hold an error value and the 'Right' constructor is used to+hold a correct value (mnemonic: \"right\" also means \"correct\").++==== __Examples__++The type @'Either' 'String' 'Int'@ is the type of values which can be either+a 'String' or an 'Int'. The 'Left' constructor can be used only on+'String's, and the 'Right' constructor can be used only on 'Int's:++>>> let s = Left "foo" :: Either String Int+>>> s+Left "foo"+>>> let n = Right 3 :: Either String Int+>>> n+Right 3+>>> :type s+s :: Either String Int+>>> :type n+n :: Either String Int++The 'fmap' from our 'Functor' instance will ignore 'Left' values, but+will apply the supplied function to values contained in a 'Right':++>>> let s = Left "foo" :: Either String Int+>>> let n = Right 3 :: Either String Int+>>> fmap (*2) s+Left "foo"+>>> fmap (*2) n+Right 6++The 'Monad' instance for 'Either' allows us to chain together multiple+actions which may fail, and fail overall if any of the individual+steps failed. First we'll write a function that can either parse an+'Int' from a 'Char', or fail.++>>> import Data.Char ( digitToInt, isDigit )+>>> :{+ let parseEither :: Char -> Either String Int+ parseEither c+ | isDigit c = Right (digitToInt c)+ | otherwise = Left "parse error"+>>> :}++The following should work, since both @\'1\'@ and @\'2\'@ can be+parsed as 'Int's.++>>> :{+ let parseMultiple :: Either String Int+ parseMultiple = do+ x <- parseEither '1'+ y <- parseEither '2'+ return (x + y)+>>> :}++>>> parseMultiple+Right 3++But the following should fail overall, since the first operation where+we attempt to parse @\'m\'@ as an 'Int' will fail:++>>> :{+ let parseMultiple :: Either String Int+ parseMultiple = do+ x <- parseEither 'm'+ y <- parseEither '2'+ return (x + y)+>>> :}++>>> parseMultiple+Left "parse error"++-}+data Either a b = Either (# a | b #)++-- | The 'Left' pattern synonym mimics the functionality of base's 'Data.Either.Left' constructor+pattern Left :: a -> Either a b+pattern Left a = Either (# a | #)++-- | The 'Right' pattern synonym mimics the functionality of base's 'Data.Either.Right' constructor+pattern Right :: b -> Either a b+pattern Right b = Either (# | b #)++{-# COMPLETE Left, Right #-}++-- | This is the same as 'Left'.+left :: a -> Either a b+left a = Either (# a | #)+{-# INLINE left #-}++-- | This is the same as 'Right'.+right :: b -> Either a b+right b = Either (# | b #)+{-# INLINE right #-}++-- | Case analysis for the 'Either' type.+-- If the value is @'Left' a@, apply the first function to @a@;+-- if it is @'Right' b@, apply the second function to @b@.+--+-- ==== __Examples__+--+-- We create two values of type @'Either' 'String' 'Int'@, one using the+-- 'Left' constructor and another using the 'Right' constructor. Then+-- we apply \"either\" the 'length' function (if we have a 'String')+-- or the \"times-two\" function (if we have an 'Int'):+--+-- >>> let s = Left "foo" :: Either String Int+-- >>> let n = Right 3 :: Either String Int+-- >>> either length (*2) s+-- 3+-- >>> either length (*2) n+-- 6+--+either :: (a -> c) -> (b -> c) -> Either a b -> c+either fa fb (Either x) = case x of+ (# a | #) -> fa a+ (# | b #) -> fb b+{-# INLINE either #-}++-- | Return `True` if the given value is a `Left`-value, `False` otherwise.+--+-- ==== __Examples__+--+-- Basic usage:+--+-- >>> isLeft (Left "foo")+-- True+-- >>> isLeft (Right 3)+-- False+--+-- Assuming a 'Left' value signifies some sort of error, we can use+-- 'isLeft' to write a very simple error-reporting function that does+-- absolutely nothing in the case of success, and outputs \"ERROR\" if+-- any error occurred.+--+-- This example shows how 'isLeft' might be used to avoid pattern+-- matching when one does not care about the value contained in the+-- constructor:+--+-- >>> import Control.Monad ( when )+-- >>> let report e = when (isLeft e) $ putStrLn "ERROR"+-- >>> report (Right 1)+-- >>> report (Left "parse error")+-- ERROR+--+isLeft :: Either a b -> Bool+isLeft = either (const True) (const False)+{-# INLINE isLeft #-}++-- | Return `True` if the given value is a `Right`-value, `False` otherwise.+--+-- ==== __Examples__+--+-- Basic usage:+--+-- >>> isRight (Left "foo")+-- False+-- >>> isRight (Right 3)+-- True+--+-- Assuming a 'Left' value signifies some sort of error, we can use+-- 'isRight' to write a very simple reporting function that only+-- outputs \"SUCCESS\" when a computation has succeeded.+--+-- This example shows how 'isRight' might be used to avoid pattern+-- matching when one does not care about the value contained in the+-- constructor:+--+-- >>> import Control.Monad ( when )+-- >>> let report e = when (isRight e) $ putStrLn "SUCCESS"+-- >>> report (Left "parse error")+-- >>> report (Right 1)+-- SUCCESS+--+isRight :: Either a b -> Bool+isRight = either (const False) (const True)+{-# INLINE isRight #-}++-- | Extracts from a list of 'Either' all the 'Left' elements.+-- All the 'Left' elements are extracted in order.+--+-- ==== __Examples__+--+-- Basic usage:+--+-- >>> let list = [ Left "foo", Right 3, Left "bar", Right 7, Left "baz" ]+-- >>> lefts list+-- ["foo","bar","baz"]+--+lefts :: [Either a b] -> [a]+lefts x = [a | Left a <- x]+{-# INLINEABLE lefts #-}++-- | Extracts from a list of 'Either' all the 'Right' elements.+-- All the 'Right' elements are extracted in order.+--+-- ==== __Examples__+--+-- Basic usage:+--+-- >>> let list = [ Left "foo", Right 3, Left "bar", Right 7, Left "baz" ]+-- >>> rights list+-- [3,7]+--+rights :: [Either a b] -> [b]+rights x = [b | Right b <- x]+{-# INLINEABLE rights #-}++-- | Partitions a list of 'Either' into two lists.+-- All the 'Left' elements are extracted, in order, to the first+-- component of the output. Similarly the 'Right' elements are extracted+-- to the second component of the output.+--+-- ==== __Examples__+--+-- Basic usage:+--+-- >>> let list = [ Left "foo", Right 3, Left "bar", Right 7, Left "baz" ]+-- >>> partitionEithers list+-- (["foo","bar","baz"],[3,7])+--+-- The pair returned by @'partitionEithers' x@ should be the same+-- pair as @('lefts' x, 'rights' x)@:+--+-- >>> let list = [ Left "foo", Right 3, Left "bar", Right 7, Left "baz" ]+-- >>> partitionEithers list == (lefts list, rights list)+-- True+--+partitionEithers :: [Either a b] -> ([a], [b])+partitionEithers = foldr' (either l r) ([],[])+ where+ l a (lft, rgt) = (a:lft, rgt)+ r a (lft, rgt) = (lft, a:rgt)++-- | Return the contents of a 'Left'-value or a default value otherwise.+--+-- ==== __Examples__+--+-- Basic usage:+--+-- >>> fromLeft 1 (Left 3)+-- 3+-- >>> fromLeft 1 (Right "foo")+-- 1+--+fromLeft :: a -> Either a b -> a+fromLeft def = either id (const def)+{-# INLINE fromLeft #-}++-- | Return the contents of a 'Right'-value or a default value otherwise.+--+-- ==== __Examples__+--+-- Basic usage:+--+-- >>> fromRight 1 (Right 3)+-- 3+-- >>> fromRight 1 (Left "foo")+-- 1+--+fromRight :: b -> Either a b -> b+fromRight def = either (const def) id+{-# INLINE fromRight #-}++-- | The 'fromBaseEither' function converts base's 'Data.Either.Either' to an+-- 'Data.Either.Unpacked.Either'. This function is good for using+-- existing functions that return base's 'Data.Either.Either' values.+--+-- ==== __Examples__+--+-- Basic usage:+--+-- >>> import Text.Read ( readEither )+-- >>> let parse = fromBaseEither . readEither :: String -> Either String Int+-- >>> parse "3"+-- Right 3+-- >>> parse ""+-- Left "Prelude.read: no parse"+--+fromBaseEither :: BaseEither.Either a b -> Either a b+fromBaseEither (BaseEither.Left a) = left a+fromBaseEither (BaseEither.Right b) = right b++-- | The 'toBaseEither' function converts an 'Either' value to a+-- value of base's 'Data.Either.Either' type.+--+-- This function is provided for testing an convenience but+-- it is not an idiomatic use of this library. It ruins the speed and space gains from+-- unpacking the 'Either'. I implore you to destruct the 'Either' with 'either' instead.+--+-- ==== __Examples__+--+-- Basic usage:+--+-- >>> import Data.List (unfoldr)+-- >>> let ana n = if n == 5 then (left "stop here") else right (n+1,n+1)+-- >>> unfoldr (toBaseMaybe . ana) 0+-- [1,2,3,4,5]+--+toBaseEither :: Either a b -> BaseEither.Either a b+toBaseEither (Left a) = BaseEither.Left a+toBaseEither (Right b) = BaseEither.Right b++--------------------------------------------------------------------------------++-- this is what happens when you can't derive things+instance (Eq a, Eq b) => Eq (Either a b) where+ Left a == Left b = a == b+ Right a == Right b = a == b+ _ == _ = False+ {-# INLINE (==) #-}++-- this is what happens when you can't derive things+instance (Ord a, Ord b) => Ord (Either a b) where+ compare x y+ = case x of+ Left a -> case y of+ Left b -> compare a b+ _ -> LT+ Right a -> case y of+ Right b -> compare a b+ _ -> GT+ {-# INLINE compare #-}++-- this is what happens when you can't derive things+instance (Read a, Read b) => Read (Either a b) where+ readPrec+ = parens (prec 10+ (do expectP (Ident "Left")+ a <- step readPrec+ return (Left a))+ ++++ prec+ 10+ (do expectP (Ident "Right")+ b <- step readPrec+ return (Right b)))+ readList = readListDefault+ readListPrec = readListPrecDefault++-- this is what happens when you can't derive things+instance (Show b, Show a) => Show (Either a b) where+ showsPrec i (Left a)+ = showParen+ (i >= 11)+ ((.)+ (showString "Left ") (showsPrec 11 a))+ showsPrec i (Right b)+ = showParen+ (i >= 11)+ ((.)+ (showString "Right ") (showsPrec 11 b))+ showList = showList__ (showsPrec 0)++instance Semigroup b => Semigroup (Either a b) where+ (<>) = liftA2 (<>)+ {-# INLINE (<>) #-}++instance Monoid b => Monoid (Either a b) where+ mempty = pure mempty+ {-# INLINE mempty #-}+ mappend = liftA2 mappend+ {-# INLINE mappend #-}++instance Functor (Either a) where+ fmap f = either left (right . f)+ {-# INLINE fmap #-}++instance Applicative (Either e) where+ pure = right+ {-# INLINE pure #-} + ef <*> ex = either left (\f -> fmap f ex) ef+ {-# INLINE (<*>) #-}++instance Monad (Either e) where+ return = right+ {-# INLINE return #-}+ ex >>= f = either left f ex + {-# INLINE (>>=) #-}++instance Foldable (Either a) where+ foldMap f e = either (const mempty) f e+ {-# INLINE foldMap #-}+ foldr f z e = either (const z) ((flip f) z) e+ {-# INLINE foldr #-}+ foldl f z e = either (const z) (f z) e+ {-# INLINE foldl #-}+ length = either (const 0) (const 1)+ {-# INLINE length #-}+ null = isLeft+ {-# INLINE null #-}+ product = either (const 0) id+ {-# INLINE product #-}+ sum = either (const 0) id+ {-# INLINE sum #-}++instance Traversable (Either a) where+ sequenceA ea = either (pure . left) (fmap right) ea+ {-# INLINE sequenceA #-}+ traverse f ea = either (pure . left) (fmap right . f) ea + {-# INLINE traverse #-}++instance (Eq a) => Eq1 (Either a) where+ liftEq = liftEq2 (==)++instance (Ord a) => Ord1 (Either a) where+ liftCompare = liftCompare2 compare++instance (Read a) => Read1 (Either a) where+ liftReadPrec = liftReadPrec2 readPrec readListPrec++ liftReadListPrec = liftReadListPrecDefault+ liftReadList = liftReadListDefault++instance (Show a) => Show1 (Either a) where+ liftShowsPrec = liftShowsPrec2 showsPrec showList++instance Eq2 Either where+ liftEq2 e1 _ (Left x) (Left y) = e1 x y+ liftEq2 _ _ (Left _) (Right _) = False+ liftEq2 _ _ (Right _) (Left _) = False+ liftEq2 _ e2 (Right x) (Right y) = e2 x y++instance Ord2 Either where+ liftCompare2 comp1 _ (Left x) (Left y) = comp1 x y+ liftCompare2 _ _ (Left _) (Right _) = LT+ liftCompare2 _ _ (Right _) (Left _) = GT+ liftCompare2 _ comp2 (Right x) (Right y) = comp2 x y++instance Read2 Either where+ liftReadPrec2 rp1 _ rp2 _ = readData $+ readUnaryWith rp1 "Left" Left <|>+ readUnaryWith rp2 "Right" Right++ liftReadListPrec2 = liftReadListPrec2Default+ liftReadList2 = liftReadList2Default++instance Show2 Either where+ liftShowsPrec2 sp1 _ _ _ d (Left x) = showsUnaryWith sp1 "Left" d x+ liftShowsPrec2 _ _ sp2 _ d (Right x) = showsUnaryWith sp2 "Right" d x++instance Bifunctor Either where+ bimap f g = either (left . f) (right . g)+ {-# INLINE bimap #-}++instance Bifoldable Either where+ bifoldMap f g = either f g + {-# INLINE bifoldMap #-}++instance Bitraversable Either where+ bitraverse f g = either (fmap left . f) (fmap right . g) + {-# INLINE bitraverse #-}
+ test/Main.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE KindSignatures #-}++module Main (main) where++import Prelude hiding (Either(..), either)++import Control.Applicative+import Control.Monad (liftM)+import Data.Functor.Classes+import Data.Either.Unpacked (Either(Left,Right), left, right)+import Data.Proxy (Proxy(..))+import Data.Semigroup (Semigroup((<>)))+import Test.QuickCheck.Classes+import Test.QuickCheck.Arbitrary+import Test.QuickCheck.Gen++main :: IO ()+main = lawsCheckMany myClassTests++myClassTests :: [(String, [Laws])]+myClassTests =+ [ ("Ground types", myLaws eitherProxy)+ , ("Higher-kinded types", myLaws1 eitherProxy1)+ ]++myLaws+ :: (Arbitrary a, Eq a, Ord a, Show a, Read a)+ => Proxy a -> [Laws]+myLaws p =+ [ eqLaws p+ , ordLaws p+ , showReadLaws p+ ]++myLaws1+ :: (Arbitrary1 a, Monad a, Functor a, Applicative a, Foldable a, Traversable a, Eq1 a, Show1 a)+ => Proxy a -> [Laws]+myLaws1 p =+ [ monadLaws p+ , functorLaws p+ , applicativeLaws p+ , foldableLaws p+ , traversableLaws p+ ]++eitherProxy2 :: Proxy Either+eitherProxy2 = Proxy++eitherProxy1 :: Proxy (Either Int)+eitherProxy1 = Proxy++eitherProxy :: Proxy (Either Int Int)+eitherProxy = Proxy++instance Semigroup Int where+ (<>) = (+)++instance Monoid Int where+ mempty = 0+ mappend = (+)++instance (Arbitrary a, Arbitrary b) => Arbitrary (Either a b) where+ arbitrary = arbitrary2+ shrink = shrink2++instance Arbitrary2 Either where+ liftArbitrary2 arbA arbB = oneof [liftM Left arbA, liftM Right arbB]++ liftShrink2 shrA _ (Left x) = [ Left x' | x' <- shrA x ]+ liftShrink2 _ shrB (Right y) = [ Right y' | y' <- shrB y ]++instance Arbitrary a => Arbitrary1 (Either a) where+ liftArbitrary = liftArbitrary2 arbitrary+ liftShrink = liftShrink2 shrink
+ unpacked-either.cabal view
@@ -0,0 +1,52 @@+--------------------------------------------------------------------------------++name: unpacked-either+version: 0.1.0.0+synopsis: An unpacked either data type+description:+ This module is intended to be a drop-in replacement for Either. To shave off pointer chasing, it uses '-XUnboxedSums' to represent the Either type as two machine words that are contiguous in memory, without loss of expressiveness that Either provides.+ .+ This library provides pattern synonyms Left and Right that allow users to pattern match on an Unpacked Either in a familiar way.+ .+ Functions are also provided for converting an Unpacked Either to the base library's Either, and vice versa.+ .+ This library is in alpha, and the internals are likely to change.+homepage: https://github.com/chessai/unpacked-either+license: BSD3+license-file: LICENSE+author: chessai+maintainer: chessai1996@gmail.com+copyright: Copyright © 2018 chessai+category: Development+build-type: Simple+cabal-version: >=1.10++--------------------------------------------------------------------------------++library+ hs-source-dirs: src+ exposed-modules: Data.Either.Unpacked+ build-depends: base >= 4.10 && < 5+ , ghc-prim+ default-language: Haskell2010++-------------------------------------------------------------------------------++test-suite unpacked-either-test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs+ build-depends: base+ , unpacked-either+ , QuickCheck+ , quickcheck-classes+ ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2+ default-language: Haskell2010++-------------------------------------------------------------------------------++source-repository head+ type: git+ location: https://github.com/chessai/unpacked-either++--------------------------------------------------------------------------------