invert 1.0.0.3 → 1.0.0.4
raw patch · 8 files changed
+350/−333 lines, 8 filesdep ~basedep ~containersdep ~generic-derivingPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, containers, generic-deriving, hashable, unordered-containers
API changes (from Hackage documentation)
Files
- benchmarks/bench.hs +17/−19
- changelog.md +4/−0
- examples/billing-codes.hs +27/−40
- invert.cabal +13/−12
- src/Invert.hs +238/−224
- src/Invert/Reexport.hs +14/−9
- src/Map.hs +35/−26
- src/Vector.hs +2/−3
benchmarks/bench.hs view
@@ -1,38 +1,36 @@-import Criterion.Main (defaultMainWith, defaultConfig, bench, bgroup, whnf, Benchmark)+import Criterion.Main (Benchmark, bench, bgroup, defaultConfig, defaultMainWith, whnf) import Criterion.Types (Config (reportFile))--import qualified Invert as I+import Invert qualified as I+import Prelude sizes :: [Integer] sizes = [100, 500, 2_000, 5_000] :: [Integer] invertGroup :: String -> I.Strategy Integer Integer -> Benchmark invertGroup name strategy =- bgroup name $- fmap (invertBench strategy) sizes+ bgroup name $+ fmap (invertBench strategy) sizes invertBench :: (Show b, Enum b, Num b) => I.Strategy b b -> b -> Benchmark invertBench strategy size =- bench (show size) $- let- f = I.function strategy [1 .. size] (* (2 ^ (10 :: Integer)))- f' x = sum (foldMap (\e -> f (x ^ (e :: Integer))) [0 .. 10])- in- whnf f' size+ bench (show size) $+ let f = I.function strategy [1 .. size] (* (2 ^ (10 :: Integer)))+ f' x = sum (foldMap (\e -> f (x ^ (e :: Integer))) [0 .. 10])+ in whnf f' size groups :: [Benchmark] groups =- [ invertGroup "linearSearchLazy" I.linearSearchLazy- , invertGroup "linearSearchStrict" I.linearSearchStrict- , invertGroup "binarySearch" I.binarySearch- , invertGroup "hashTable" I.hashTable- ]+ [ invertGroup "linearSearchLazy" I.linearSearchLazy,+ invertGroup "linearSearchStrict" I.linearSearchStrict,+ invertGroup "binarySearch" I.binarySearch,+ invertGroup "hashTable" I.hashTable+ ] config :: Config config =- defaultConfig- { reportFile = Just "bench.html"- }+ defaultConfig+ { reportFile = Just "bench.html"+ } main :: IO () main = defaultMainWith config groups
changelog.md view
@@ -1,3 +1,7 @@+### 1.0.0.4 (2023-06-26)++Raise language to GHC2021+ ### 1.0.0.3 (2023-01-11) Packaging and documentation improvement
examples/billing-codes.hs view
@@ -1,45 +1,33 @@-{-# LANGUAGE DeriveGeneric #-}- -- Enables stock deriving of the Generic class--{-# LANGUAGE DeriveAnyClass #-}- -- Enables deriving the GEnum class--{-# LANGUAGE DerivingStrategies #-}- -- Lets us explicitly say how we want to derive;- -- e.g. "deriving stock" or "deriving anyclass"- import Invert-+import Prelude import System.Exit (die) data Product = Basic | Standard | Pro- deriving stock (Generic, Show, Eq)- deriving anyclass GEnum+ deriving stock (Generic, Show, Eq)+ deriving anyclass (GEnum) data Frequency = Monthly | Annual- deriving stock (Generic, Show, Eq)- deriving anyclass GEnum+ deriving stock (Generic, Show, Eq)+ deriving anyclass (GEnum) data Bill = Bill Product Frequency- deriving stock (Generic, Show, Eq)- deriving anyclass GEnum+ deriving stock (Generic, Show, Eq)+ deriving anyclass (GEnum) encodeProduct :: Product -> String encodeProduct x = case x of-- Basic -> "p1"- Standard -> "p2"- Pro -> "p3"+ Basic -> "p1"+ Standard -> "p2"+ Pro -> "p3" encodeBill :: Bill -> Integer encodeBill x = case x of-- Bill Basic Monthly -> 10- Bill Basic Annual -> 11- Bill Standard Monthly -> 20- Bill Standard Annual -> 21- Bill Pro Monthly -> 30- Bill Pro Annual -> 31+ Bill Basic Monthly -> 10+ Bill Basic Annual -> 11+ Bill Standard Monthly -> 20+ Bill Standard Annual -> 21+ Bill Pro Monthly -> 30+ Bill Pro Annual -> 31 decodeProduct :: String -> Maybe Product decodeProduct = Invert.injection hashTable genum encodeProduct@@ -49,20 +37,19 @@ main :: IO () main = do-- encodeProduct Basic === "p1"- encodeProduct Standard === "p2"+ encodeProduct Basic === "p1"+ encodeProduct Standard === "p2" - decodeProduct "p1" === Just Basic- decodeProduct "xyz" === Nothing+ decodeProduct "p1" === Just Basic+ decodeProduct "xyz" === Nothing - encodeBill (Bill Basic Annual) === 11- encodeBill (Bill Pro Monthly) === 30+ encodeBill (Bill Basic Annual) === 11+ encodeBill (Bill Pro Monthly) === 30 - decodeBill 31 === Just (Bill Pro Annual)- decodeBill 50 === Nothing+ decodeBill 31 === Just (Bill Pro Annual)+ decodeBill 50 === Nothing (===) :: (Eq a, Show a) => a -> a -> IO ()--a === b | a == b = pure ()- | otherwise = die (show a <> " /= " <> show b)+a === b+ | a == b = pure ()+ | otherwise = die (show a <> " /= " <> show b)
invert.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: invert-version: 1.0.0.3+version: 1.0.0.4 synopsis: Automatically generate a function’s inverse category: Functions @@ -23,14 +23,20 @@ extra-source-files: *.md common base- default-language: Haskell2010+ default-language: GHC2021 ghc-options: -Wall+ default-extensions:+ DeriveAnyClass+ DerivingStrategies+ ExistentialQuantification+ NamedFieldPuns+ NoImplicitPrelude build-depends:- , base ^>= 4.14 || ^>= 4.15 || ^>= 4.16 || ^>= 4.17- , containers ^>= 0.6.4- , hashable ^>= 1.3.5 || ^>= 1.4- , unordered-containers ^>= 0.2.17- , generic-deriving ^>= 1.14.1+ , base ^>= 4.16 || ^>= 4.17 || ^>= 4.18+ , containers ^>= 0.6.5+ , hashable ^>= 1.4.2+ , unordered-containers ^>= 0.2.19+ , generic-deriving ^>= 1.14.4 , vector ^>= 0.12.3 || ^>= 0.13 library@@ -42,10 +48,6 @@ Map Vector hs-source-dirs: src- default-extensions:- NoImplicitPrelude- NamedFieldPuns- ExistentialQuantification test-suite billing-codes-example import: base@@ -57,7 +59,6 @@ benchmark invert-benchmark import: base- default-extensions: NumericUnderscores type: exitcode-stdio-1.0 hs-source-dirs: benchmarks main-is: bench.hs
src/Invert.hs view
@@ -1,203 +1,217 @@-{-# language Safe #-}+{-# LANGUAGE Safe #-} module Invert- (- {- * Overview -} {- $overview -}+ ( -- * Overview+ -- $overview - {- * 1. Varieties of function -}- function, bijection, injection, surjection,+ -- * 1. Varieties of function+ function,+ bijection,+ injection,+ surjection, - {- * 2. Inversion strategies -} linearSearchLazy,- linearSearchStrict, binarySearch, hashTable,+ -- * 2. Inversion strategies+ linearSearchLazy,+ linearSearchStrict,+ binarySearch,+ hashTable, - {- * 3. Domain enumeration -} enumBounded, genum,+ -- * 3. Domain enumeration+ enumBounded,+ genum, - {- * The Strategy type -} Strategy, {- $strategyCreation -}- strategyAll, strategyOneAndAll,+ -- * The Strategy type+ Strategy,+ -- $strategyCreation+ strategyAll,+ strategyOneAndAll, - {- * Re-exports -} {- $reexports -} module Invert.Reexport,+ -- * Re-exports+ -- $reexports+ module Invert.Reexport, )- where--import Invert.Reexport--import qualified Map-import Map (Map (Map))--import qualified Vector+where import Data.Eq (Eq, (==)) import Data.Foldable (foldl') import Data.Function ((.))+import Data.List qualified as List (lookup, map) import Data.List.NonEmpty (NonEmpty, nonEmpty) import Data.Maybe (Maybe (Just, Nothing), fromMaybe, listToMaybe)+import Data.Maybe qualified as List (mapMaybe) import Data.Ord (Ord) import Data.Tuple (uncurry)-import Prelude (error)-import Prelude (Enum, enumFromTo)-import Prelude (Bounded, minBound, maxBound)--import qualified Data.List as List (lookup, map)-import qualified Data.Maybe as List (mapMaybe)-import qualified Generics.Deriving as GEnum (genum)--{- $overview--There are three considerations when you’re inverting a function:-- 1. Is it an injection, a surjection, both (a bijection), or neither?- 2. What data structure do you want to use for efficient lookups?- 3. Can you produce a list of all values in the function’s domain?--=== 1. What sort of function do you have?--This question determines the type of the function’s inverse.--For a function @(a -> b)@, we call @(a)@ its /domain/, and @(b)@ its /codomain/.-- * In general, when you invert a 'function' of type @(a -> b)@,- the type of the inverse is @(b -> [a])@.- The result is a list because it contains all domain values that- map to a given codomain value; there may be none, one, or many.-- * If your function @(a -> b)@ is a 'bijection',- you can invert it to get a function @(b -> a)@.- Bijections are quite pleasing in this way.-- * If no two domain values map to the same codomain value,- then your function is an 'injection',- and it has an inverse of type @(b -> 'Maybe' a)@.-- * If every codomain value has some domain value that maps to it,- then your function is a 'surjection',- and it has an inverse of type @(b -> 'NonEmpty' a)@.--You are responsible for determining which is appropriate for a particular-situation: 'function', 'bijection', 'injection', or 'surjection'.-Choose carefully; the wrong choice may produce an inverse which is-partial or incorrect.--=== 2. How can we produce a reasonably efficient inversion?--The simplest inversion strategies, 'linearSearchLazy' and 'linearSearchStrict',-apply the function to each element of the domain, one by one.-We call this a /linear search/ because the time required for each-application has a linear correspondence with the size of the domain.-- * 'linearSearchStrict' works by precomputing a strict sequence- of tuples, one for each value of the domain.-- * 'linearSearchLazy' precomputes nothing at all.- It is possible to use this strategy when the domain is infinite.--Our other two strategies, 'binarySearch' and 'hashTable',-work by building data structures that allow more efficient lookups.-- * 'binarySearch' precomputes a binary search tree;- the codomain must belong to the 'Ord' class.-- * 'hashTable' precomputes a hash table;- the codomain must belong to the 'Hashable' class.--The 'Hashable' class comes from "Data.Hashable" in the @hashable@ package.-The class is re-exported by "Invert", which you may find convenient if-your primary motivation for deriving 'Hashable' is to invert a function.--=== 3. How will you enumerate the domain?--Inverting a function @(a -> b)@ requires having a list of all-possible values of domain @(a)@; from this, we can apply the-function to every value to produce a list of tuples that-completely describes the function.--We offer two suggestions for automatically producing this list:-- * 'enumBounded' uses two stock-derivable classes, 'Enum' and 'Bounded'.- * 'genum' uses GHC generics; it requires deriving 'Generic' and 'GEnum'.+import Generics.Deriving qualified as GEnum (genum)+import Invert.Reexport+import Map (Map (Map))+import Map qualified+import Vector qualified+import Prelude (Bounded, Enum, enumFromTo, error, maxBound, minBound) -The 'Generic' class comes from "GHC.Generics", and the 'GEnum' class-comes from "Generics.Deriving" in the @generic-deriving@ package.-Both classes are re-exported by "Invert", which you may find convenient-if your primary motivation for deriving 'GEnum' is to invert a function. -}+-- $overview+--+-- There are three considerations when you’re inverting a function:+--+-- 1. Is it an injection, a surjection, both (a bijection), or neither?+-- 2. What data structure do you want to use for efficient lookups?+-- 3. Can you produce a list of all values in the function’s domain?+--+-- === 1. What sort of function do you have?+--+-- This question determines the type of the function’s inverse.+--+-- For a function @(a -> b)@, we call @(a)@ its /domain/, and @(b)@ its /codomain/.+--+-- * In general, when you invert a 'function' of type @(a -> b)@,+-- the type of the inverse is @(b -> [a])@.+-- The result is a list because it contains all domain values that+-- map to a given codomain value; there may be none, one, or many.+--+-- * If your function @(a -> b)@ is a 'bijection',+-- you can invert it to get a function @(b -> a)@.+-- Bijections are quite pleasing in this way.+--+-- * If no two domain values map to the same codomain value,+-- then your function is an 'injection',+-- and it has an inverse of type @(b -> 'Maybe' a)@.+--+-- * If every codomain value has some domain value that maps to it,+-- then your function is a 'surjection',+-- and it has an inverse of type @(b -> 'NonEmpty' a)@.+--+-- You are responsible for determining which is appropriate for a particular+-- situation: 'function', 'bijection', 'injection', or 'surjection'.+-- Choose carefully; the wrong choice may produce an inverse which is+-- partial or incorrect.+--+-- === 2. How can we produce a reasonably efficient inversion?+--+-- The simplest inversion strategies, 'linearSearchLazy' and 'linearSearchStrict',+-- apply the function to each element of the domain, one by one.+-- We call this a /linear search/ because the time required for each+-- application has a linear correspondence with the size of the domain.+--+-- * 'linearSearchStrict' works by precomputing a strict sequence+-- of tuples, one for each value of the domain.+--+-- * 'linearSearchLazy' precomputes nothing at all.+-- It is possible to use this strategy when the domain is infinite.+--+-- Our other two strategies, 'binarySearch' and 'hashTable',+-- work by building data structures that allow more efficient lookups.+--+-- * 'binarySearch' precomputes a binary search tree;+-- the codomain must belong to the 'Ord' class.+--+-- * 'hashTable' precomputes a hash table;+-- the codomain must belong to the 'Hashable' class.+--+-- The 'Hashable' class comes from "Data.Hashable" in the @hashable@ package.+-- The class is re-exported by "Invert", which you may find convenient if+-- your primary motivation for deriving 'Hashable' is to invert a function.+--+-- === 3. How will you enumerate the domain?+--+-- Inverting a function @(a -> b)@ requires having a list of all+-- possible values of domain @(a)@; from this, we can apply the+-- function to every value to produce a list of tuples that+-- completely describes the function.+--+-- We offer two suggestions for automatically producing this list:+--+-- * 'enumBounded' uses two stock-derivable classes, 'Enum' and 'Bounded'.+-- * 'genum' uses GHC generics; it requires deriving 'Generic' and 'GEnum'.+--+-- The 'Generic' class comes from "GHC.Generics", and the 'GEnum' class+-- comes from "Generics.Deriving" in the @generic-deriving@ package.+-- Both classes are re-exported by "Invert", which you may find convenient+-- if your primary motivation for deriving 'GEnum' is to invert a function. function ::- Strategy a b- -> [a] -- ^ A complete list of all the values of the domain.- -> (a -> b) -- ^ The function to invert.- -> (b -> [a]) -- ^ The inverse of the given function.-+ Strategy a b ->+ -- | A complete list of all the values of the domain.+ [a] ->+ -- | The function to invert.+ (a -> b) ->+ -- | The inverse of the given function.+ (b -> [a]) bijection ::- Strategy a b- -> [a]- -- ^ A complete list of all the values of the domain.- -> (a -> b)- -- ^ The function to invert.- -- __This function must be bijective!__- -- This means that every value in the codomain has- -- exactly one value in the domain that maps to it.- -> (b -> a)- -- ^ The inverse of the given function.-+ Strategy a b ->+ -- | A complete list of all the values of the domain.+ [a] ->+ -- | The function to invert.+ -- __This function must be bijective!__+ -- This means that every value in the codomain has+ -- exactly one value in the domain that maps to it.+ (a -> b) ->+ -- | The inverse of the given function.+ (b -> a) injection ::- Strategy a b- -> [a]- -- ^ A complete list of all the values of the domain.- -> (a -> b)- -- ^ The function to invert.- -- __This function must be injective!__- -- This means that no two values in the domain map- -- to the same value of the codomain.- -> (b -> Maybe a)- -- ^ The inverse of the given function.-+ Strategy a b ->+ -- | A complete list of all the values of the domain.+ [a] ->+ -- | The function to invert.+ -- __This function must be injective!__+ -- This means that no two values in the domain map+ -- to the same value of the codomain.+ (a -> b) ->+ -- | The inverse of the given function.+ (b -> Maybe a) surjection ::- Strategy a b- -> [a]- -- ^ A complete list of all the values of the domain.- -> (a -> b)- -- ^ The function to invert.- -- __This function must be surjective!__- -- This means that every value in the codomain has- -- at least one value in the domain that maps to it.- -> (b -> NonEmpty a)- -- ^ The inverse of the given function.-+ Strategy a b ->+ -- | A complete list of all the values of the domain.+ [a] ->+ -- | The function to invert.+ -- __This function must be surjective!__+ -- This means that every value in the codomain has+ -- at least one value in the domain that maps to it.+ (a -> b) ->+ -- | The inverse of the given function.+ (b -> NonEmpty a) function (Strategy _ s) as f = s (inverseEntries as f)-injection (Strategy s _) as f = s (inverseEntries as f)-bijection (Strategy s _) as f = finagle . s (inverseEntries as f)- where finagle = fromMaybe (error "Not a bijection!")-surjection (Strategy _ s) as f = finagle . s (inverseEntries as f)- where finagle = fromMaybe (error "Not a surjection!") . nonEmpty -{-| An inversion strategy is an approach for producing- the inverse of an @(a -> b)@ function+injection (Strategy s _) as f = s (inverseEntries as f) -All strategies produce the same results, but they-have operational differences that affect performance. -}-data Strategy a b =- Strategy- ([(b, a)] -> b -> Maybe a)- ([(b, a)] -> b -> [a])+bijection (Strategy s _) as f = finagle . s (inverseEntries as f)+ where+ finagle = fromMaybe (error "Not a bijection!") -{- $strategyCreation+surjection (Strategy _ s) as f = finagle . s (inverseEntries as f)+ where+ finagle = fromMaybe (error "Not a surjection!") . nonEmpty -=== Defining your own strategies+-- | An inversion strategy is an approach for producing+-- the inverse of an @(a -> b)@ function+--+-- All strategies produce the same results, but they+-- have operational differences that affect performance.+data Strategy a b+ = Strategy+ ([(b, a)] -> b -> Maybe a)+ ([(b, a)] -> b -> [a]) -If you want to design your own strategy instead-of using one provided by this module, use either-'strategyAll' or 'strategyOneAndAll'. -}+-- $strategyCreation+--+-- === Defining your own strategies+--+-- If you want to design your own strategy instead+-- of using one provided by this module, use either+-- 'strategyAll' or 'strategyOneAndAll'. strategyAll ::- ([(b, a)] -> b -> [a]) -- ^ Find all matches- -> Strategy a b+ -- | Find all matches+ ([(b, a)] -> b -> [a]) ->+ Strategy a b strategyAll all = strategyOneAndAll one all where one bas b = listToMaybe (all bas b) strategyOneAndAll ::- ([(b, a)] -> b -> Maybe a) -- ^ Find the first match- -> ([(b, a)] -> b -> [a]) -- ^ Find all matches- -> Strategy a b+ -- | Find the first match+ ([(b, a)] -> b -> Maybe a) ->+ -- | Find all matches+ ([(b, a)] -> b -> [a]) ->+ Strategy a b strategyOneAndAll = Strategy inverseEntries :: [a] -> (a -> b) -> [(b, a)]@@ -206,23 +220,23 @@ mapStrategy :: Map Maybe b a -> Map [] b a -> Strategy a b mapStrategy one all = Strategy (f one) (f all) where- f Map{ Map.empty, Map.singleton, Map.union, Map.lookup } =- lookup . foldl' union empty . List.map (uncurry singleton)--{-| A function inversion strategy that precomputes nothing at all+ f Map {Map.empty, Map.singleton, Map.union, Map.lookup} =+ lookup . foldl' union empty . List.map (uncurry singleton) -It is possible to use this strategy when the domain is infinite. -}+-- | A function inversion strategy that precomputes nothing at all+--+-- It is possible to use this strategy when the domain is infinite. linearSearchLazy :: Eq b => Strategy a b linearSearchLazy = Strategy one all where one bas b = List.lookup b bas all bas b = List.mapMaybe (sndIfFstEq b) bas -{-| A function inversion strategy that works by precomputing a- strict sequence of tuples, one for each value of the domain--For larger functions, it may be preferable to use 'binarySearch' or-'hashTable' instead to get a more efficient inverse. -}+-- | A function inversion strategy that works by precomputing a+-- strict sequence of tuples, one for each value of the domain+--+-- For larger functions, it may be preferable to use 'binarySearch' or+-- 'hashTable' instead to get a more efficient inverse. linearSearchStrict :: Eq b => Strategy a b linearSearchStrict = strategyAll f where@@ -233,64 +247,64 @@ sndIfFstEq :: Eq b => b -> (b, a) -> Maybe a sndIfFstEq x (b, a) = if b == x then Just a else Nothing -{-| A function inversion strategy that works by precomputing- a binary search tree--The data structure imposes the requirement that the codomain-belongs to the 'Ord' class. -}+-- | A function inversion strategy that works by precomputing+-- a binary search tree+--+-- The data structure imposes the requirement that the codomain+-- belongs to the 'Ord' class. binarySearch :: Ord b => Strategy a b binarySearch = mapStrategy Map.ordSingleMap Map.ordMultiMap -{-| A function inversion strategy that works by precomputing- a hash table--The data structure imposes the requirement that the codomain-belongs to the 'Hashable' class. -}+-- | A function inversion strategy that works by precomputing+-- a hash table+--+-- The data structure imposes the requirement that the codomain+-- belongs to the 'Hashable' class. hashTable :: (Eq b, Hashable b) => Strategy a b hashTable = mapStrategy Map.hashSingleMap Map.hashMultiMap -{-| A convenient way to enumerate the domain for a function that you-want to invert, using the stock-derivable classes 'Enum' and 'Bounded'--To derive the required typeclass instances, add the following deriving clause to-the type’s definition:--@-deriving (Enum, Bounded)-@ -}+-- | A convenient way to enumerate the domain for a function that you+-- want to invert, using the stock-derivable classes 'Enum' and 'Bounded'+--+-- To derive the required typeclass instances, add the following deriving clause to+-- the type’s definition:+--+-- @+-- deriving (Enum, Bounded)+-- @ enumBounded :: (Enum a, Bounded a) => [a] enumBounded = enumFromTo minBound maxBound -{-| Use GHC generics to enumerate a function's domain--This requires deriving 'Generic' and 'GEnum'. The 'Generic' class comes-from "GHC.Generics", and the 'GEnum' class comes from "Generics.Deriving"-in the @generic-deriving@ package.--To derive the required typeclass instances, enable the following-language extensions:--@-\{\-# language DeriveGeneric, DeriveAnyClass, DerivingStrategies #\-\}-@--Then add the following deriving clauses to the type’s definition:--@-deriving stock Generic-deriving anyclass GEnum-@ -}+-- | Use GHC generics to enumerate a function's domain+--+-- This requires deriving 'Generic' and 'GEnum'. The 'Generic' class comes+-- from "GHC.Generics", and the 'GEnum' class comes from "Generics.Deriving"+-- in the @generic-deriving@ package.+--+-- To derive the required typeclass instances, enable the following+-- language extensions:+--+-- @+-- \{\-# language DeriveGeneric, DeriveAnyClass, DerivingStrategies #\-\}+-- @+--+-- Then add the following deriving clauses to the type’s definition:+--+-- @+-- deriving stock Generic+-- deriving anyclass GEnum+-- @ genum :: GEnum a => [a] genum = GEnum.genum -{- $reexports--This module provides a few definitions that come directly from-other packages. These are here to let you conveniently derive-'Hashable' and 'GEnum' with only the "Invert" module imported.--List of re-exports:-- - __'Hashable'__ (for the 'hashTable' inversion strategy)- - __'Generic'__ and __'GEnum'__ (for the 'genum' domain- enumeration approach) -}+-- $reexports+--+-- This module provides a few definitions that come directly from+-- other packages. These are here to let you conveniently derive+-- 'Hashable' and 'GEnum' with only the "Invert" module imported.+--+-- List of re-exports:+--+-- - __'Hashable'__ (for the 'hashTable' inversion strategy)+-- - __'Generic'__ and __'GEnum'__ (for the 'genum' domain+-- enumeration approach)
src/Invert/Reexport.hs view
@@ -1,12 +1,17 @@-{-# language Safe #-}+{-# LANGUAGE Safe #-} module Invert.Reexport- (- {- * Hashable -} Hashable,- {- * Generic -} Generic,- {- * GEnum -} GEnum- ) where+ ( -- * Hashable+ Hashable, -import Data.Hashable ( Hashable )-import Generics.Deriving ( GEnum )-import GHC.Generics ( Generic )+ -- * Generic+ Generic,++ -- * GEnum+ GEnum,+ )+where++import Data.Hashable (Hashable)+import GHC.Generics (Generic)+import Generics.Deriving (GEnum)
src/Map.hs view
@@ -1,38 +1,47 @@-{-# language Safe #-}+{-# LANGUAGE Safe #-} module Map where -import Data.Eq ( Eq )-import Data.Hashable ( Hashable )-import Data.Maybe ( Maybe, maybe )-import Data.Ord ( Ord )--import qualified Data.Foldable- as Seq (toList)--import qualified Data.HashMap.Strict- as HashMap (lookup, singleton, empty, union, unionWith)--import qualified Data.Map.Strict- as OrdMap (lookup, singleton, empty, union, unionWith)--import qualified Data.Sequence- as Seq (singleton, (><))+import Data.Eq (Eq)+import Data.Foldable qualified as Seq+ ( toList,+ )+import Data.HashMap.Strict qualified as HashMap+ ( empty,+ lookup,+ singleton,+ union,+ unionWith,+ )+import Data.Hashable (Hashable)+import Data.Map.Strict qualified as OrdMap+ ( empty,+ lookup,+ singleton,+ union,+ unionWith,+ )+import Data.Maybe (Maybe, maybe)+import Data.Ord (Ord)+import Data.Sequence qualified as Seq+ ( singleton,+ (><),+ ) data Map f a b = forall map. Map- { empty :: map- , singleton :: a -> b -> map- , union :: map -> map -> map- , lookup :: map -> a -> f b- }+ { empty :: map,+ singleton :: a -> b -> map,+ union :: map -> map -> map,+ lookup :: map -> a -> f b+ } type SingleMap = Map Maybe type MultiMap = Map [] hashSingleMap :: (Eq a, Hashable a) => SingleMap a b-hashSingleMap = Map{ empty, singleton, union, lookup }+hashSingleMap = Map {empty, singleton, union, lookup} where empty = HashMap.empty singleton = HashMap.singleton@@ -40,7 +49,7 @@ lookup m a = HashMap.lookup a m hashMultiMap :: (Eq a, Hashable a) => MultiMap a b-hashMultiMap = Map{ empty, singleton, union, lookup }+hashMultiMap = Map {empty, singleton, union, lookup} where empty = HashMap.empty singleton = \a b -> HashMap.singleton a (Seq.singleton b)@@ -48,7 +57,7 @@ lookup = \m a -> maybe [] Seq.toList (HashMap.lookup a m) ordSingleMap :: Ord a => SingleMap a b-ordSingleMap = Map{ empty, singleton, union, lookup }+ordSingleMap = Map {empty, singleton, union, lookup} where empty = OrdMap.empty singleton = OrdMap.singleton@@ -56,7 +65,7 @@ lookup m a = OrdMap.lookup a m ordMultiMap :: Ord a => MultiMap a b-ordMultiMap = Map{ empty, singleton, union, lookup }+ordMultiMap = Map {empty, singleton, union, lookup} where empty = OrdMap.empty singleton = \a b -> OrdMap.singleton a (Seq.singleton b)
src/Vector.hs view
@@ -1,11 +1,10 @@-{-# language Trustworthy #-}+{-# LANGUAGE Trustworthy #-} module Vector (fromList, toList, mapMaybe) where import Data.Maybe (Maybe)- import Data.Vector (Vector)-import qualified Data.Vector as V+import Data.Vector qualified as V fromList :: [a] -> Vector a fromList = V.fromList