packages feed

isotope 0.3.3.0 → 0.4.0.0

raw patch · 6 files changed

+91/−75 lines, 6 filesdep ~megaparsecPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: megaparsec

API changes (from Hackage documentation)

- Isotope: class ToCondensedFormuala a
- Isotope: lookupElement :: ElementSymbol -> Maybe Element
+ Isotope: class ToElementalComposition a => ToCondensedFormula a
- Isotope: class ToEmpiricalFormula a
+ Isotope: class ToElementalComposition a => ToEmpiricalFormula a
- Isotope: class ToMolecularFormula a
+ Isotope: class ToElementalComposition a => ToMolecularFormula a
- Isotope: selectIsotope :: ElementSymbol -> MassNumber -> Isotope
+ Isotope: selectIsotope :: ElementSymbol -> MassNumber -> Maybe Isotope
- Isotope: toCondensedFormula :: ToCondensedFormuala a => a -> CondensedFormula
+ Isotope: toCondensedFormula :: ToCondensedFormula a => a -> CondensedFormula

Files

README.md view
@@ -9,7 +9,7 @@     * [Elemental composition and molecular, condensed and empirical formulae](#elemental-composition-and-molecular-condensed-and-empirical-formulae)     * [ElementalComposition, MolecularFormula, CondensedFormula, EmpiricalFormula quasiquoters](#elementalcomposition-molecularformula-condensedformula-empiricalformula-quasiquoters)     * [Conversion between ElementalComposition, MolecularFormula, CondensedFormula and EmpiricalFormula data types](#conversion-between-elementalcomposition-molecularformula-condensedformula-and-empiricalformula-data-types)-    * [Operators for working with molecular formulae](#operators-for-working-with-molecular-formulae)+    * [Operators for working with formulae and masses](#operators-for-working-with-formulae-and-masses)     * [ToElementalComposition type class](#elementalcomposition-type-class)     * [Behaviour of ElementalComposition, MolecularFormula, CondensedFormula and EmpiricalFormula data types](#behaviour-of-elementalcomposition-molecularformula-condensedformula-and-empiricalformula-data-types)     * [Additional functions accepting an ElementSymbol as input](#additional-functions-accepting-an-elementsymbol-as-input)@@ -108,7 +108,7 @@  ### `ToElementalComposition` type class -In addition to the `toElementalComposition` method, the `ToElementalComposition` type class has three other methods; `monoisotopicMass`, `nominalMass` and `averageMass`. (`toElementalComposition` is the minimal complete definition.) `ElementSymbol`, `ElementalComposition`, `MolecularFormula`, `CondensedFormula` and `EmpiricalFormula` all have instances of `ToElementalComposition`. This provides a uniform approach to working with elements, elemental compositions, molecular formulae, condensed formulae and empirical formulae.+`ToElementalComposition` is a superclass of `ToMolecularFormula`, `ToCondensedFormula` and `ToEmpiricalFormula`. In addition to the `toElementalComposition` method, the `ToElementalComposition` type class has three other methods; `monoisotopicMass`, `nominalMass` and `averageMass`. (`toElementalComposition` is the minimal complete definition.) `ElementSymbol`, `ElementalComposition`, `MolecularFormula`, `CondensedFormula` and `EmpiricalFormula` all have instances of `ToElementalComposition`. This provides a uniform approach to working with elements, elemental compositions, molecular formulae, condensed formulae and empirical formulae. ```haskell ghci> nominalMass C NominalMass {getNominalMass = 12}@@ -135,7 +135,7 @@  #### Laws for `ElementalComposition`, `MolecularFormula`, `EmpiricalFormula` and `CondensedFormula` data types -Instances of `ToElementalComposition`, `ToMolecularFormula`, `ToCondensedFormuala` and `ToEmpiricalFormula` should abide by three laws. 1) Applying `toEmpiricalFormula` to a `CondensedFormula` should give the same result as applying `toMolecularFormula` compose `toEmpiricalFormula`.+Instances of `ToElementalComposition`, `ToMolecularFormula`, `ToCondensedFormula` and `ToEmpiricalFormula` should abide by three laws. 1) Applying `toEmpiricalFormula` to a `CondensedFormula` should give the same result as applying `toMolecularFormula` compose `toEmpiricalFormula`. 2) Applying `toElementalComposition` to a `CondensedFormula` should give the same result as applying `toMolecularFormula` compose `toElementalComposition`. 3) Applying `toElementalComposition . toEmpiricalFormula` to an `EmpiricalFormula` should return the same `EmpiricalFormula`. 
isotope.cabal view
@@ -1,5 +1,5 @@ name:                isotope-version:             0.3.3.0+version:             0.4.0.0 synopsis:            Isotopic masses and relative abundances. description:         Please see README.md homepage:            https://github.com/Michaelt293/Element-isotopes/blob/master/README.md@@ -20,7 +20,7 @@                      , Isotope.Parsers   build-depends:       base >= 4.7 && < 5                      , containers >= 0.5 && < 0.6-                     , megaparsec >= 4 && < 6+                     , megaparsec >= 5 && < 6                      , template-haskell                      , th-lift   default-language:    Haskell2010@@ -34,7 +34,7 @@                      , isotope                      , hspec                      , QuickCheck-                     , megaparsec >= 4 && < 6+                     , megaparsec   other-modules:       Isotope.BaseSpec                      , Isotope.ParsersSpec   ghc-options:         -threaded -rtsopts -with-rtsopts=-N
src/Isotope.hs view
@@ -47,7 +47,6 @@   -- * 'elements' - a map containing isotopic data for each element.   , elements   -- * Functions taking an 'elementSymbol' as input-  , lookupElement   , findElement   , elementName   , atomicNumber@@ -69,7 +68,7 @@   , mkMolecularFormula   -- * Condensed formulae   , CondensedFormula(..)-  , ToCondensedFormuala(..)+  , ToCondensedFormula(..)   -- * Empirical formulae   , EmpiricalFormula(..)   , ToEmpiricalFormula(..)
src/Isotope/Base.hs view
@@ -1,7 +1,7 @@ {-| Module      : Isotope.Base-Description : Contains most of the data type declarations used in the Isotope-              library.+Description : Contains the data type and type class declarations used in the+              Isotope library. Copyright   : Michael Thomas License     : GPL-3 Maintainer  : Michael Thomas <Michaelt293@gmail.com>@@ -53,7 +53,6 @@     -- 'elements' - a map containing isotopic data for each element.     , elements     -- Functions taking an 'elementSymbol' as input-    , lookupElement     , findElement     , elementName     , atomicNumber@@ -69,33 +68,24 @@     , ElementalComposition(..)     , ToElementalComposition(..)     , mkElementalComposition-    -- Molecular formulae+    -- Molecular formula     , MolecularFormula(..)     , ToMolecularFormula(..)     , mkMolecularFormula-    -- Condensed formulae+    -- Condensed formula     , CondensedFormula(..)-    , ToCondensedFormuala(..)+    , ToCondensedFormula(..)     -- Empirical formula     , EmpiricalFormula(..)     , ToEmpiricalFormula(..)     , mkEmpiricalFormula     ) where -import Prelude hiding      (lookup,filter)-import Data.Map            ( Map-                           , fromList-                           , unionWith-                           , filter-                           , mapWithKey-                           , lookup-                           , (!)-                           , toList-                           )-import Data.Foldable hiding (toList)+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Foldable import Data.Ord-import Data.List           (elemIndex, sortBy)-import Data.Maybe          (fromJust)+import Data.List import Data.Monoid  --------------------------------------------------------------------------------@@ -118,7 +108,7 @@  -- | The exact mass of the most abundant isotope for an element or the sum of -- the exact masses of the most abundant isotope of each element for a--- molecular formula.+-- elemental composition. newtype MonoisotopicMass = MonoisotopicMass { getMonoisotopicMass :: Double }                          deriving (Show, Eq, Ord) @@ -132,8 +122,8 @@   MonoisotopicMass x |*| y = MonoisotopicMass $ x * fromIntegral y  -- | The integer mass of the most abundant isotope for an element or the sum of--- integer mass of the most abundant isotope of each element for a chemical--- formula.+-- integer mass of the most abundant isotope of each element for a elemental+-- composition. newtype NominalMass = NominalMass { getNominalMass :: Int }                     deriving (Show, Eq, Ord) @@ -146,7 +136,7 @@   NominalMass x |-| NominalMass y = NominalMass $ x - y   NominalMass x |*| y = NominalMass $ x * y --- | The average mass of an element or molecular formula based on+-- | The average mass of an element or elemental composition based on -- naturally-occurring abundances. newtype AverageMass = AverageMass { getAverageMass :: Double }                     deriving (Show, Eq, Ord)@@ -284,14 +274,15 @@ tupleToIsotope (nucl, mass, abun) =   Isotope nucl (IsotopicMass mass) (IsotopicAbundance abun) -tupleToElement :: (AtomicNumber, ElementName, [(Nucleons, Double, Double)]) -> Element+tupleToElement+  :: (AtomicNumber, ElementName, [(Nucleons, Double, Double)]) -> Element tupleToElement (atomNum, name, isotopes) =   Element atomNum name (tupleToIsotope <$> isotopes)  -- | Map of the periodic table. All data on isotopic masses and abundances is -- contained within this map. elements :: Map ElementSymbol Element-elements = tupleToElement <$> fromList+elements = tupleToElement <$> Map.fromList   [ (H,  (1, "hydrogen",      [ ((1, 0),     1.00782503223,  0.999885)                               , ((1, 1),     2.01410177812,  0.000115) ]))   , (He, (2,  "helium",       [ ((2, 1),     3.0160293201,   0.00000134)@@ -585,15 +576,10 @@ -------------------------------------------------------------------------------- -- Functions taking an 'elementSymbol' as input --- | Searches elements (a map) with an 'ElementSymbol' key and returns--- information for the element (wrapped in 'Maybe').-lookupElement :: ElementSymbol -> Maybe Element-lookupElement = flip lookup elements- -- | Searches 'elements' (a map) with an 'ElementSymbol' key and returns -- information for the element. findElement :: ElementSymbol -> Element-findElement = (!) elements+findElement = (Map.!) elements  -- | Returns the name for an element symbol. elementName :: ElementSymbol -> ElementName@@ -612,11 +598,11 @@ mostAbundantIsotope = elementMostAbundantIsotope . findElement  -- | Selects an isotope of element based on the isotope's mass number--- ('IntegerMass'). Note: This is a partial function.-selectIsotope :: ElementSymbol -> MassNumber -> Isotope-selectIsotope sym mass = isotopeList !! indexOfIsotope+-- ('IntegerMass').+selectIsotope :: ElementSymbol -> MassNumber -> Maybe Isotope+selectIsotope sym massNum =+  find (\iso -> (massNumber . nucleons) iso ==  massNum) isotopeList     where isotopeList = isotopes sym-          indexOfIsotope = fromJust $ elemIndex mass (integerMasses sym)  -- | Exact masses for all naturally-occurring isotopes for an element. isotopicMasses :: ElementSymbol -> [IsotopicMass]@@ -633,7 +619,7 @@ -------------------------------------------------------------------------------- -- Formula type class --- | Type class with two methods, 'renderFormula' and 'emptyFormula'. The+-- | Type class with two methods; 'renderFormula' and 'emptyFormula'. The -- 'renderFormula' method converts a formula to its shorthand notation. class Formula a where     renderFormula :: a -> String@@ -665,12 +651,12 @@ getFormulaSum :: (Monoid a, Operators a, ToElementalComposition b)   => (Element -> a) -> b -> a getFormulaSum f m = fold $-    mapWithKey mapFunc (getElementalComposition (toElementalComposition m))+    Map.mapWithKey mapFunc (getElementalComposition (toElementalComposition m))   where mapFunc k v = (f . findElement) k |*| v  -- | Smart constructor to make values of type 'ElementalComposition'. mkElementalComposition :: [(ElementSymbol, Int)] -> ElementalComposition-mkElementalComposition = ElementalComposition . filterZero . fromList+mkElementalComposition = ElementalComposition . filterZero . Map.fromList  instance Monoid ElementalComposition where   mempty = emptyFormula@@ -706,16 +692,16 @@ sortElementSymbolMap :: Map ElementSymbol Int -> [(ElementSymbol, Int)] sortElementSymbolMap m = sortBy (hillSystem fst) elementSymbolIntList     where-      elementSymbolIntList = toList m+      elementSymbolIntList = Map.toList m       elementSymbols = fst <$> elementSymbolIntList       containsC = C `elem` elementSymbols       hillSystem f a b = case (f a, f b) of         (C, _)   -> LT         (_, C)   -> GT         (H, b')  -> if containsC then LT-                    else (show . elementName) H `compare` show b'+                    else show H `compare` show b'         (a', H)  -> if containsC then GT-                    else show a' `compare` (show . elementName) H+                    else show a' `compare` show H         (a', b') -> show a' `compare` show b'  --------------------------------------------------------------------------------@@ -726,21 +712,21 @@     getMolecularFormula :: Map ElementSymbol Int }         deriving (Show, Read, Eq, Ord) -class ToMolecularFormula a where+class ToElementalComposition a => ToMolecularFormula a where     toMolecularFormula :: a -> MolecularFormula  -- The function unionWith adapted to work with 'Map ElementSymbol Int'. combineMaps :: (Int -> Int -> Int)   -> Map ElementSymbol Int ->  Map ElementSymbol Int  ->  Map ElementSymbol Int-combineMaps f m1 m2 = filterZero $ unionWith f m1 m2+combineMaps f m1 m2 = filterZero $ Map.unionWith f m1 m2  -- | Smart constructor to make values of type 'MolecularFormula'. mkMolecularFormula :: [(ElementSymbol, Int)] -> MolecularFormula-mkMolecularFormula = MolecularFormula . filterZero . fromList+mkMolecularFormula = MolecularFormula . filterZero . Map.fromList  -- Helper function to remove (k, v) pairs where v == 0. filterZero :: Map k Int -> Map k Int-filterZero = filter (/= 0)+filterZero = Map.filter (/= 0)  instance Monoid MolecularFormula where    mempty = emptyFormula@@ -770,7 +756,7 @@     getCondensedFormula :: [Either MolecularFormula (CondensedFormula, Int)] }         deriving (Show, Read, Eq, Ord) -class ToCondensedFormuala a where+class ToElementalComposition a => ToCondensedFormula a where   toCondensedFormula :: a -> CondensedFormula  instance Monoid CondensedFormula where@@ -804,15 +790,15 @@     getEmpiricalFormula :: Map ElementSymbol Int }         deriving (Show, Read, Eq, Ord) --- | Type class with a single method, 'toEmpiricalFormula', which converts a+-- | Type class with a single method; 'toEmpiricalFormula', which converts a -- chemical data type to `EmpiricalFormula`.-class ToEmpiricalFormula a where+class ToElementalComposition a => ToEmpiricalFormula a where   toEmpiricalFormula :: a -> EmpiricalFormula  -- | Smart constructor to make values of type 'EmpiricalFormula'. mkEmpiricalFormula :: [(ElementSymbol, Int)] -> EmpiricalFormula mkEmpiricalFormula l =-  let m = filterZero (fromList l)+  let m = filterZero (Map.fromList l)   in EmpiricalFormula $ (`div` greatestCommonDenom m) <$> m  instance ToEmpiricalFormula ElementalComposition where
src/Isotope/Parsers.hs view
@@ -29,12 +29,12 @@  import Isotope.Base import Language.Haskell.TH.Quote+import Language.Haskell.TH.Syntax import Language.Haskell.TH.Lift import Text.Megaparsec import Text.Megaparsec.String import qualified Text.Megaparsec.Lexer as L-import Data.String-import Data.List hiding (filter)+import Data.List import Data.Map (Map) import Data.Monoid ((<>)) @@ -61,7 +61,8 @@  -- | Parses a condensed formula, i.e., \"N(CH3)3\". condensedFormula :: Parser CondensedFormula-condensedFormula =  CondensedFormula <$> many (leftCondensedFormula <|> rightCondensedFormula)+condensedFormula =+  CondensedFormula <$> many (leftCondensedFormula <|> rightCondensedFormula)   where     subMolecularFormula :: Parser MolecularFormula     subMolecularFormula = mkMolecularFormula . pure <$> subFormula@@ -80,48 +81,76 @@ empiricalFormula = mkEmpiricalFormula <$> many subFormula  -- Helper function for `ElementalComposition` quasiquoter+quoteElementalComposition :: String -> Q Exp quoteElementalComposition s =   case parse (condensedFormula <* eof) "" s of-    Left err -> error $ "Could not parse formula: " <> show err+    Left err -> fail $+      "Could not parse elemental formula!\n" <> parseErrorPretty err     Right v  -> lift $ toElementalComposition v  -- Helper function for `MolecularFormula` quasiquoter+quoteMolecularFormula :: String -> Q Exp quoteMolecularFormula s =   case parse (condensedFormula <* eof) "" s of-    Left err -> fail $ "Could not parse formula: " <> show err+    Left err -> fail $+      "Could not parse molecular formula!\n" <> parseErrorPretty err     Right v  -> lift $ toMolecularFormula v  -- Helper function for `CondensedFormula` quasiquoter+quoteCondensedFormula :: String -> Q Exp quoteCondensedFormula s =   case parse (condensedFormula <* eof) "" s of-    Left err -> error $ "Could not parse formula: " <> show err+    Left err -> fail $+      "Could not parse condensed formula!\n" <> parseErrorPretty err     Right v  -> lift v  -- Helper function for `EmpiricalFormula` quasiquoter quoteEmpiricalFormula s =   case parse (condensedFormula <* eof) "" s of-    Left err -> fail $ "Could not parse formula: " <> show err+    Left err -> fail $+      "Could not parse empirical formula!\n" <> parseErrorPretty err     Right v  -> lift $ toEmpiricalFormula v  -- | Quasiquoter for `ElementalComposition` ele :: QuasiQuoter-ele = QuasiQuoter-    { quoteExp = quoteElementalComposition }+ele = QuasiQuoter {+    quoteExp = quoteElementalComposition+  , quotePat  = notHandled "patterns" "elemental composition"+  , quoteType = notHandled "types" "elemental composition"+  , quoteDec  = notHandled "declarations" "elemental composition"+  }  -- | Quasiquoter for `MolecularFormula` mol :: QuasiQuoter-mol = QuasiQuoter-    { quoteExp = quoteMolecularFormula }+mol = QuasiQuoter {+    quoteExp = quoteMolecularFormula+  , quotePat  = notHandled "patterns" "molecular formula"+  , quoteType = notHandled "types" "molecular formula"+  , quoteDec  = notHandled "declarations" "molecular formula"+  }  -- | Quasiquoter for `CondensedFormula` con :: QuasiQuoter-con = QuasiQuoter-    { quoteExp = quoteCondensedFormula }+con = QuasiQuoter {+    quoteExp = quoteCondensedFormula+  , quotePat  = notHandled "patterns" "condensed formula"+  , quoteType = notHandled "types" "condensed formula"+  , quoteDec  = notHandled "declarations" "condensed formula"+  }  -- | Quasiquoter for `EmpiricalFormula` emp :: QuasiQuoter-emp = QuasiQuoter-    { quoteExp = quoteEmpiricalFormula }+emp = QuasiQuoter {+    quoteExp = quoteEmpiricalFormula+  , quotePat  = notHandled "patterns" "empirical formula"+  , quoteType = notHandled "types" "empirical formula"+  , quoteDec  = notHandled "declarations" "empirical formula"+  }++-- Helper function used in QuasiQuoters+notHandled :: String -> String -> a+notHandled feature quoterName =+  error $ feature <> " are not handled by the" <> quoterName <> "quasiquoter."  $(deriveLift ''ElementSymbol) 
test/Isotope/BaseSpec.hs view
@@ -18,9 +18,9 @@         (\x -> length x /= 2 || (isLower . last) x) . show <$>         elementSymbolList `shouldSatisfy` and -    describe "lookupElement" .+    describe "elementSymbolList" .       it "should not contain duplicate elements" $-        lookupElement <$> elementSymbolList `shouldSatisfy` allUnique+        findElement <$> elementSymbolList `shouldSatisfy` allUnique      describe "elementName" $ do       it "should not be an empty string" $@@ -47,7 +47,7 @@      describe "selectIsotope" .       it "calling fuction with the arguments C and 12 should select C12" $-        nucleons (selectIsotope C 12) `shouldBe` (6, 6)+        nucleons <$> selectIsotope C 12 `shouldBe` Just (6, 6)      describe "monoisotopicMass" .       it "calling function with C should be 12.0" $@@ -93,6 +93,8 @@         renderFormula [mol|CCl4|] `shouldBe` "CCl4"       it "[mol|H2O4S|] should be \"H2O4S\"" $         renderFormula [mol|H2O4S|] `shouldBe` "H2O4S"+      it "[mol|BBr3|] should be \"BBr3\"" $+        renderFormula [mol|BBr3|] `shouldBe` "BBr3"      describe "renderFormula for CondensedFormula" $ do       it "[con|C6H6O|] should be \"C6H6O\"" $