tax 0.2.0.0 → 0.2.1.0
raw patch · 2 files changed
+116/−66 lines, 2 filesdep −semigroupsdep ~basePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies removed: semigroups
Dependency ranges changed: base
API changes (from Hackage documentation)
+ Data.Tax: infixr 6 <>
- Data.Tax: Tax :: a -> b -> Tax a b
+ Data.Tax: Tax :: (a -> b) -> Tax a b
- Data.Tax: class Profunctor (p :: * -> * -> *)
+ Data.Tax: class Profunctor (p :: Type -> Type -> Type)
- Data.Tax: dimap :: Profunctor p => a -> b -> c -> d -> p b c -> p a d
+ Data.Tax: dimap :: Profunctor p => (a -> b) -> (c -> d) -> p b c -> p a d
- Data.Tax: effective :: (Fractional a) => Money a -> Tax (Money a) (Money a) -> Tax (Money a) (Money a)
+ Data.Tax: effective :: Fractional a => Money a -> Tax (Money a) (Money a) -> Tax (Money a) (Money a)
- Data.Tax: flat :: (Num a) => a -> Tax (Money a) (Money a)
+ Data.Tax: flat :: Num a => a -> Tax (Money a) (Money a)
- Data.Tax: greaterOf :: (Ord a) => Tax b a -> Tax b a -> Tax b a
+ Data.Tax: greaterOf :: Ord a => Tax b a -> Tax b a -> Tax b a
- Data.Tax: lesserOf :: (Ord a) => Tax b a -> Tax b a -> Tax b a
+ Data.Tax: lesserOf :: Ord a => Tax b a -> Tax b a -> Tax b a
- Data.Tax: limit :: (Ord a) => a -> Tax b a -> Tax b a
+ Data.Tax: limit :: Ord a => a -> Tax b a -> Tax b a
- Data.Tax: lmap :: Profunctor p => a -> b -> p b c -> p a c
+ Data.Tax: lmap :: Profunctor p => (a -> b) -> p b c -> p a c
- Data.Tax: marginal :: (Fractional a, Ord a) => [(Money a, a)] -> Tax (Money a) (Money a)
+ Data.Tax: marginal :: (Num a, Ord a) => [(Money a, a)] -> Tax (Money a) (Money a)
- Data.Tax: rmap :: Profunctor p => b -> c -> p a b -> p a c
+ Data.Tax: rmap :: Profunctor p => (b -> c) -> p a b -> p a c
- Data.Tax: thresholds :: (Fractional a, Ord a) => [(Money a, a)] -> Tax (Money a) (Money a)
+ Data.Tax: thresholds :: (Num a, Ord a) => [(Money a, a)] -> Tax (Money a) (Money a)
- Data.Tax: type MoneyTax a = Tax (Money a) (Money a)
+ Data.Tax: type MoneyTax a = Tax Money a Money a
Files
- src/Data/Tax.hs +81/−58
- tax.cabal +35/−8
src/Data/Tax.hs view
@@ -14,12 +14,50 @@ -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see <http://www.gnu.org/licenses/>. +{-# LANGUAGE GeneralizedNewtypeDeriving #-}+ {-| This library provides combinators for constructing taxes. It is based on the <https://hackage.haskell.org/package/dollaridoos dollaridoos> library. +-}+module Data.Tax+ (+ -- * Overview+ -- $doc++ -- * API+ Tax(..)+ , MoneyTax+ , lump+ , flat+ , threshold+ , threshold'+ , thresholds+ , above+ , above'+ , marginal+ , lesserOf+ , greaterOf+ , limit+ , effective++ -- * Re-exports+ , module Data.Money+ , Semigroup(..)+ , Monoid(..)+ , Profunctor(..)+ ) where++import Data.Profunctor (Profunctor(..))+import Data.Semigroup (Semigroup(..))++import Data.Money++{- $doc+ The most basic tax is a flat rate tax: @@@ -29,31 +67,41 @@ To compute the tax, use 'getTax': @-λ> 'getTax' businessTax (review money 1000000)+λ> 'getTax' businessTax (Money 1000000) $300000.0 @ Taxes form a semigroup (sum of tax outputs) and monoid: @-λ> getTax (flat 0.1 <> flat 0.2) (review money 10)+λ> getTax (flat 0.1 <> flat 0.2) (Money 10) $3.0-λ> getTax mempty (review money 10)+λ> getTax mempty (Money 10) $0 @ -Marginal tax rates can be constructed using the 'above' combinator,+Progressive taxes can be constructed using the 'above' combinator, which taxes the amount above a given threshold at a flat rate. @ individualIncomeTax =- 'above' (review money 18200) 0.19- <> 'above' (review money 37000) (0.325 - 0.19)- <> 'above' (review money 87000) (0.37 - 0.325)- <> 'above' (review money 180000) (0.45 - 0.37)+ 'above' (Money 18200 ) (0.19 - 0 )+ <> 'above' (Money 45000 ) (0.325 - 0.19 )+ <> 'above' (Money 120000) (0.37 - 0.325 )+ <> 'above' (Money 180000) (0.45 - 0.37 ) @ -Taxes can be negative. For exmaple, the 'lump', 'above' and 'limit'+The 'marginal' function provides a shorthand for the above.++@+individualIncomeTax = 'marginal'+ [ ( Money 18200, 0.19 - 0 )+ , ( Money 45000, 0.325 - 0.19 )+ , ( Money 120000, 0.37 - 0.325 )+ , ( Money 180000, 0.45 - 0.37 ) ]+@++Taxes can be negative. For example, the 'lump', 'above' and 'limit' combinators can be used to construct a low-income tax offset that starts at $445 and reduces at a rate of 1.5c per dollar earned over $37000:@@ -61,21 +109,28 @@ @ lowIncomeTaxOffset = 'limit' mempty- ('lump' (review money (-445)) <> 'above' (review money 37000) 0.015)+ ('lump' (Money (-445)) <> 'above' (Money 37000) 0.015) @ The 'threshold' combinator applies a tax to the full input amount,-if it exceeds the threshold. Some taxes have "shade-in" where the-amount above the threshold is taxed at a higher rate to "catch up"-to some lower flat rate. The 'threshold'' and 'lesserOf'-combinators can be used to construct this tax:+if it exceeds the threshold. @-medicareLevy =- 'threshold'' l ('lesserOf' ('above' l 0.1) ('flat' 0.02))- where l = review money 21656+medicareLevySurcharge =+ 'threshold' (review money 90000 ) 0.0100+ <> 'threshold' (review money 105000) 0.0025+ <> 'threshold' (review money 140000) 0.0025 @ +Some taxes have "shade-in" where the amount above some threshold is+taxed at a higher rate to "catch up" to some lower flat rate. The+'above' and 'lesserOf' combinators can be used to construct this+tax:++@+medicareLevy l = 'lesserOf' ('above' l 0.1) ('flat' 0.02)+@+ Although some of the combinators deal directory with 'Money', a 'Tax' can be defined for other types. For example, you can tax a person a certain number of days labour, based on their age.@@ -95,40 +150,8 @@ -} -{-# LANGUAGE GeneralizedNewtypeDeriving #-}--module Data.Tax- (- -- * Constructing taxes- Tax(..)- , MoneyTax- , lump- , flat- , threshold- , threshold'- , thresholds- , above- , above'- , marginal- , lesserOf- , greaterOf- , limit- , effective-- -- * Miscellanea- , Semigroup(..)- , Monoid(..)- , Profunctor(..)- , module Data.Money- ) where--import Data.Monoid (Monoid(..))-import Data.Profunctor (Profunctor(..))-import Data.Semigroup (Semigroup(..))--import Data.Money---- | A function from gross income to tax payable.+-- | A function from an amount or value subject to taxation, to+-- the amount or value of tax due. -- -- Taxes form a semigroup where the tax payable is the -- sum of tax payable of consituent taxes.@@ -154,15 +177,15 @@ above' :: (Num b, Ord b) => Money b -> Tax (Money b) a -> Tax (Money b) a above' l = lmap (\x -> max (x $-$ l) mempty) --- | Convert a @[(threshold, rate)]@ into a marginal tax.+-- | Convert a @[(threshold, rate)]@ into a progressive tax. -- The rates are /cumulative/, i.e. the top marginal rate is the -- sum of the rates that apply for a given input. ---marginal :: (Fractional a, Ord a) => [(Money a, a)] -> Tax (Money a) (Money a)+marginal :: (Num a, Ord a) => [(Money a, a)] -> Tax (Money a) (Money a) marginal = foldMap (uncurry above) --- | A lump-sum tax; a fixed value, not affected by the size of the input+-- | A lump-sum tax; a fixed value (ignores input). -- lump :: a -> Tax b a lump = Tax . const@@ -186,10 +209,10 @@ -- extra 10% at 50000: -- -- @--- tax = thresholds [(30000, .2), (50000, .1)]+-- tax = thresholds [(30000, 0.2), (50000, 0.1)] -- @ ---thresholds :: (Fractional a, Ord a) => [(Money a, a)] -> Tax (Money a) (Money a)+thresholds :: (Num a, Ord a) => [(Money a, a)] -> Tax (Money a) (Money a) thresholds = foldMap (uncurry threshold) -- | Levy the lesser of two taxes@@ -202,8 +225,8 @@ -- | Limit the tax payable to the given amount ----- This could be used e.g. for limiting a compulsory loan--- repayment to the balance of the loan, or ensuring a+-- This could be used e.g. to limit a compulsory loan+-- repayment to the balance of the loan, or ensure a -- (negative) tax offset does not become a (positive) tax. -- limit :: (Ord a) => a -> Tax b a -> Tax b a
tax.cabal view
@@ -1,19 +1,19 @@+cabal-version: 2.2 name: tax-version: 0.2.0.0+version: 0.2.1.0 synopsis: Types and combinators for taxes description: This library provides combinators for constructing taxes. It is based on the <https://hackage.haskell.org/package/dollaridoos dollaridoos> library.-license: AGPL-3+license: AGPL-3.0-or-later license-file: LICENSE author: Fraser Tweedale maintainer: frase@frase.id.au copyright: Copyright (C) 2018 Fraser Tweedale category: Finance build-type: Simple-cabal-version: >=1.10 tested-with:- GHC==7.10.3, GHC==8.0.2, GHC==8.2.1, GHC==8.4.3+ GHC ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.8 || ==9.6.7 || ==9.8.4 || ==9.10.2 || ==9.12.2 homepage: https://github.com/frasertweedale/hs-tax bug-reports: https://github.com/frasertweedale/hs-tax/issues@@ -21,16 +21,43 @@ type: git location: https://github.com/frasertweedale/hs-tax.git +common common+ default-language: Haskell2010+ ghc-options:+ -Wall+ -Wcompat+ -Werror=missing-methods+ -Widentities+ -Wincomplete-record-updates+ -Wincomplete-uni-patterns+ -Wmissing-export-lists+ -Wnoncanonical-monad-instances+ -Wpartial-fields+ -Wredundant-constraints+ -Wunused-packages+ -fhide-source-paths+ if impl(ghc >= 9.0)+ ghc-options:+ -Winvalid-haddock+ -Werror=unicode-bidirectional-format-characters+ if impl(ghc >= 9.2)+ ghc-options:+ -Wimplicit-lift+ -Woperator-whitespace+ -Wredundant-bang-patterns+ if impl(ghc >= 9.4)+ ghc-options:+ -Wredundant-strictness-flags+ build-depends:+ base >= 4.14 && < 5+ library+ import: common exposed-modules: Data.Tax -- other-modules: -- other-extensions: build-depends:- base >= 4.8 && < 5 , dollaridoos >= 0.1 , profunctors >= 5.1- , semigroups >= 0.16 hs-source-dirs: src- default-language: Haskell2010- ghc-options: -Wall