packages feed

monoid-subclasses 1.2.1 → 1.2.2

raw patch · 5 files changed

+279/−23 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Data.Monoid.LCM: class GCDMonoid m => LCMMonoid m
+ Data.Monoid.LCM: instance (Data.Monoid.LCM.LCMMonoid a, Data.Monoid.LCM.LCMMonoid b) => Data.Monoid.LCM.LCMMonoid (a, b)
+ Data.Monoid.LCM: instance (Data.Monoid.LCM.LCMMonoid a, Data.Monoid.LCM.LCMMonoid b, Data.Monoid.LCM.LCMMonoid c) => Data.Monoid.LCM.LCMMonoid (a, b, c)
+ Data.Monoid.LCM: instance (Data.Monoid.LCM.LCMMonoid a, Data.Monoid.LCM.LCMMonoid b, Data.Monoid.LCM.LCMMonoid c, Data.Monoid.LCM.LCMMonoid d) => Data.Monoid.LCM.LCMMonoid (a, b, c, d)
+ Data.Monoid.LCM: instance Data.Monoid.LCM.LCMMonoid ()
+ Data.Monoid.LCM: instance Data.Monoid.LCM.LCMMonoid (Data.Semigroup.Internal.Product GHC.Num.Natural.Natural)
+ Data.Monoid.LCM: instance Data.Monoid.LCM.LCMMonoid (Data.Semigroup.Internal.Sum GHC.Num.Natural.Natural)
+ Data.Monoid.LCM: instance Data.Monoid.LCM.LCMMonoid Data.IntSet.Internal.IntSet
+ Data.Monoid.LCM: instance Data.Monoid.LCM.LCMMonoid a => Data.Monoid.LCM.LCMMonoid (Data.Semigroup.Internal.Dual a)
+ Data.Monoid.LCM: instance GHC.Classes.Ord a => Data.Monoid.LCM.LCMMonoid (Data.Set.Internal.Set a)
+ Data.Monoid.LCM: lcm :: LCMMonoid m => m -> m -> m

Files

CHANGELOG.md view
@@ -1,3 +1,8 @@+Version 1.2.2+---------------+* Add `Data.Monoid.LCM` module with `LCMMonoid` type class by Jonathan Knowles+* Repair links to Hackage within `README.md` by Jonathan Knowles+ Version 1.2.1 --------------- * Fix for the `Monus` instance for `Maybe` by Jonathan Knowles
README.md view
@@ -4,37 +4,38 @@ ### Subclasses of Semigroup and Monoid with a solid theoretical foundation and practical purposes ###  The monoid-subclasses package has been released [on-Hackage](http://hackage.haskell.org/package/monoid-subclasses). The package defines several classes that are richer-than [semigroups](http://hackage.haskell.org/package/base/docs/Data-Semigroup.html#t:Semigroup) and-[monoids](http://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Monoid) but less demanding than-[groups](http://hackage.haskell.org/package/groups/docs/Data-Group.html):+Hackage](https://hackage.haskell.org/package/monoid-subclasses). The package defines several classes that are richer+than [semigroups](https://hackage.haskell.org/package/base/docs/Data-Semigroup.html#t:Semigroup) and+[monoids](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Monoid) but less demanding than+[groups](https://hackage.haskell.org/package/groups/docs/Data-Group.html): -* [Reductive](http://hackage.haskell.org/package/monoid-subclasses/docs/Data-Semigroup-Cancellative.html#t:Reductive)+* [Reductive](https://hackage.haskell.org/package/monoid-subclasses/docs/Data-Semigroup-Cancellative.html#t:Reductive) provides the operator `</>` which acts as a partial inverse of the semigroup `<>` operator.-* [Cancellative](http://hackage.haskell.org/package/monoid-subclasses/docs/Data-Semigroup-Cancellative.html#t:Cancellative)+* [Cancellative](https://hackage.haskell.org/package/monoid-subclasses/docs/Data-Semigroup-Cancellative.html#t:Cancellative) is a subclass of `Reductive` that provides additional guarantees about the `</>` operation result:          (a <> b) </> a == Just b         (a <> b) </> b == Just a      Every group (*i.e.*, every `Monoid a` with the operation `inverse :: a -> a`) is a cancellative monoid where `a </> b = Just (a <> inverse b)` but not every `Cancellative` monoid is a group.-    -* [GCDMonoid](http://hackage.haskell.org/package/monoid-subclasses/docs/Data-Monoid-GCD.html#t:GCDMonoid) is a subclass of `Reductive` and `Monoid` that provides the `gcd` operation for getting the greatest common denominator for two given monoid values.-* [Monus](http://hackage.haskell.org/package/monoid-subclasses/docs/Data-Monus.html#t:Monus) provides the `<\>` monus operation. The set difference is one familiar instance of this operation.-* [MonoidNull](http://hackage.haskell.org/package/monoid-subclasses/docs/Data-Monoid-Null.html) class provides the Boolean `null` operation that checks if the argument monoid is `mempty`.-* [Factorial](http://hackage.haskell.org/package/monoid-subclasses/docs/Data-Semigroup-Factorial.html) and [FactorialMonoid](http://hackage.haskell.org/package/monoid-subclasses/docs/Data-Monoid-Factorial.html) classes represent semigroups and monoids that can be split up into irreducible factors. -That's the theoretical point of view. From the practical point of view, the main purpose of the _monoid-subclasses_ package is similar to that of [ListLike](http://hackage.haskell.org/package/ListLike/docs/Data-ListLike.html) - to provide unifying abstractions for various monoidal data types in Haskell, primarily [String](http://hackage.haskell.org/package/base/docs/Data-String.html#t:String), [ByteString](http://hackage.haskell.org/package/bytestring/docs/Data-ByteString.html#t:ByteString), and [Text](http://hackage.haskell.org/package/text). All three types are already instances of the [Monoid](http://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Monoid) class. While that abstraction is useful for building sequences of data, it doesn't help with deconstructing them.+* [GCDMonoid](https://hackage.haskell.org/package/monoid-subclasses/docs/Data-Monoid-GCD.html#t:GCDMonoid) is a subclass of `Reductive` and `Monoid` that provides the `gcd` operation for getting the greatest common denominator for two given monoid values.+* [LCMMonoid](https://hackage.haskell.org/package/monoid-subclasses/docs/Data-Monoid-LCM.html#t:LCMMonoid) is a subclass of `Reductive` and `Monoid` that provides the `lcm` operation for getting the least common multiple for two given monoid values.+* [Monus](https://hackage.haskell.org/package/monoid-subclasses/docs/Data-Monoid-Monus.html#t:Monus) provides the `<\>` monus operation. The set difference is one familiar instance of this operation.+* [MonoidNull](https://hackage.haskell.org/package/monoid-subclasses/docs/Data-Monoid-Null.html#t:MonoidNull) class provides the Boolean `null` operation that checks if the argument monoid is `mempty`.+* [Factorial](https://hackage.haskell.org/package/monoid-subclasses/docs/Data-Semigroup-Factorial.html#t:Factorial) and [FactorialMonoid](https://hackage.haskell.org/package/monoid-subclasses/docs/Data-Monoid-Factorial.html#t:FactorialMonoid) classes represent semigroups and monoids that can be split up into irreducible factors. +That's the theoretical point of view. From the practical point of view, the main purpose of the _monoid-subclasses_ package is similar to that of [ListLike](https://hackage.haskell.org/package/ListLike/docs/Data-ListLike.html) - to provide unifying abstractions for various monoidal data types in Haskell, primarily [String](https://hackage.haskell.org/package/base/docs/Data-String.html#t:String), [ByteString](https://hackage.haskell.org/package/bytestring/docs/Data-ByteString.html#t:ByteString), and [Text](https://hackage.haskell.org/package/text). All three types are already instances of the [Monoid](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Monoid) class. While that abstraction is useful for building sequences of data, it doesn't help with deconstructing them.+ That being said, there are two major differences in the goals of _ListLike_ and _monoid-subclasses_:-  * _ListLike_ strives to reproduce the standard [Data.List](http://hackage.haskell.org/package/base/docs/Data-List.html) interface, whereas _monoid-subclasses_ builds from deeper theoretical foundations; and+  * _ListLike_ strives to reproduce the standard [Data.List](https://hackage.haskell.org/package/base/docs/Data-List.html) interface, whereas _monoid-subclasses_ builds from deeper theoretical foundations; and   * The _monoid-subclasses_ implementation uses standard Haskell 2010, with the exception of two minor extensions which can be worked around if necessary. -The [incremental-parser](http://hackage.haskell.org/package/incremental-parser) package can serve as a compact example+The [incremental-parser](https://hackage.haskell.org/package/incremental-parser) package can serve as a compact example of a parser library that can be applied to different input types thanks to _monoid-subclasses_. There is also-[picoparsec](http://hackage.haskell.org/package/picoparsec), a fork of-[attoparsec](http://hackage.haskell.org/package/attoparsec), and the heavy-duty-[grammatical-parsers](http://hackage.haskell.org/package/grammatical-parsers) library.+[picoparsec](https://hackage.haskell.org/package/picoparsec), a fork of+[attoparsec](https://hackage.haskell.org/package/attoparsec), and the heavy-duty+[grammatical-parsers](https://hackage.haskell.org/package/grammatical-parsers) library.  A more thorough description of the library design can be found in the Haskell Symposium 2013 paper [Adding Structure to Monoids ](https://github.com/blamario/monoid-subclasses/wiki/Files/HaskellSymposium2013.pdf)
Test/TestMonoidSubclasses.hs view
@@ -6,14 +6,15 @@  {-# LANGUAGE CPP, Rank2Types, ScopedTypeVariables, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving #-} {-# LANGUAGE ExistentialQuantification #-}+{- HLINT ignore "Use camelCase" -}  module Main where -import Prelude hiding (foldl, foldr, gcd, length, null, reverse, span, splitAt, takeWhile)+import Prelude hiding (foldl, foldr, gcd, lcm, length, null, reverse, span, splitAt, takeWhile)  import Test.Tasty (defaultMain, testGroup) import Test.Tasty.QuickCheck (Arbitrary, CoArbitrary, Property, Gen,-                              arbitrary, coarbitrary, property, label, forAll, mapSize, testProperty, variant, whenFail, (.&&.))+                              arbitrary, coarbitrary, property, label, forAll, mapSize, testProperty, variant, whenFail, (.&&.), (===)) import Test.QuickCheck.Instances ()  import Control.Applicative (Applicative(..), liftA2)@@ -73,6 +74,7 @@ import Data.Monoid.GCD (GCDMonoid, LeftGCDMonoid, RightGCDMonoid, gcd,                         commonPrefix, stripCommonPrefix,                         commonSuffix, stripCommonSuffix)+import Data.Monoid.LCM (LCMMonoid, lcm) import Data.Monoid.Monus (OverlappingGCDMonoid, Monus,                           (<\>), overlap, stripOverlap, stripPrefixOverlap, stripSuffixOverlap) import Data.Monoid.Textual (TextualMonoid)@@ -96,6 +98,7 @@           | RightGCDTest (RightGCDMonoidInstance -> Property)           | GCDTest (GCDMonoidInstance -> Property)           | CancellativeGCDTest (CancellativeGCDMonoidInstance -> Property)+          | LCMTest (LCMMonoidInstance -> Property)  data CommutativeMonoidInstance = forall a. (Arbitrary a, Show a, Eq a, Commutative a, Monoid a) =>                                  CommutativeMonoidInstance a@@ -138,6 +141,8 @@                          GCDMonoidInstance a data CancellativeGCDMonoidInstance = forall a. (Arbitrary a, Show a, Eq a, Monoid a, Cancellative a, GCDMonoid a) =>                                      CancellativeGCDMonoidInstance a+data LCMMonoidInstance = forall a. (Arbitrary a, Show a, Eq a, LCMMonoid a) =>+                         LCMMonoidInstance a  commutativeInstances :: [CommutativeMonoidInstance] commutativeInstances = map upcast reductiveInstances@@ -369,6 +374,22 @@  cancellativeGCDInstances = [CancellativeGCDMonoidInstance ()] +lcmInstances =+    [LCMMonoidInstance (mempty :: Product Natural),+     LCMMonoidInstance (mempty :: Sum Natural),+     LCMMonoidInstance (mempty :: Dual (Product Natural)),+     LCMMonoidInstance (mempty :: Dual (Sum Natural)),+     LCMMonoidInstance (mempty :: IntSet),+     LCMMonoidInstance (mempty :: (IntSet, IntSet)),+     LCMMonoidInstance (mempty :: (IntSet, IntSet, IntSet)),+     LCMMonoidInstance (mempty :: (IntSet, IntSet, IntSet, IntSet)),+     -- For sets, test with a variety of different universe sizes, from small+     -- to large:+     LCMMonoidInstance (mempty :: Set ()),+     LCMMonoidInstance (mempty :: Set Bool),+     LCMMonoidInstance (mempty :: Set Ordering),+     LCMMonoidInstance (mempty :: Set Word8)]+ main = defaultMain (testGroup "MonoidSubclasses" $ map expand tests)   where expand (name, test) = testProperty name (foldr1 (.&&.) $ checkInstances test) @@ -391,6 +412,7 @@ checkInstances (RightGCDTest checkType) = (map checkType rightGCDInstances)  checkInstances (GCDTest checkType) = (map checkType gcdInstances)   checkInstances (CancellativeGCDTest checkType) = (map checkType cancellativeGCDInstances) +checkInstances (LCMTest checkType) = (map checkType lcmInstances)  tests :: [(String, Test)] tests = [("CommutativeMonoid", CommutativeTest checkCommutative),@@ -478,7 +500,21 @@          ("stripCommonSuffix 3", RightGCDTest checkStripCommonSuffix3),          ("stripCommonSuffix 4", RightGCDTest checkStripCommonSuffix4),          ("gcd", GCDTest checkGCD),-         ("cancellative gcd", CancellativeGCDTest checkCancellativeGCD)+         ("cancellative gcd", CancellativeGCDTest checkCancellativeGCD),+         ("lcm reductivity (left)", LCMTest checkLCM_reductivity_left),+         ("lcm reductivity (right)", LCMTest checkLCM_reductivity_right),+         ("lcm uniqueness", LCMTest checkLCM_uniqueness),+         ("lcm idempotence", LCMTest checkLCM_idempotence),+         ("lcm identity (left)", LCMTest checkLCM_identity_left),+         ("lcm identity (right)", LCMTest checkLCM_identity_right),+         ("lcm commutativity", LCMTest checkLCM_commutativity),+         ("lcm associativity", LCMTest checkLCM_associativity),+         ("lcm absorption (gcd-lcm)", LCMTest checkLCM_absorption_gcd_lcm),+         ("lcm absorption (lcm-gcd)", LCMTest checkLCM_absorption_lcm_gcd),+         ("lcm distributivity (left)", LCMTest checkLCM_distributivity_left),+         ("lcm distributivity (right)", LCMTest checkLCM_distributivity_right),+         ("lcm distributivity (gcd-lcm)", LCMTest checkLCM_distributivity_gcd_lcm),+         ("lcm distributivity (lcm-gcd)", LCMTest checkLCM_distributivity_lcm_gcd)         ]  checkCommutative (CommutativeMonoidInstance (e :: a)) = forAll (arbitrary :: Gen (a, a)) (\(a, b)-> a <> b == b <> a)@@ -883,6 +919,77 @@                            && commonSuffix (a <> c) (b <> c) == (commonSuffix a b) <> c                            && gcd (a <> b) (a <> c) == a <> gcd b c                            && gcd (a <> c) (b <> c) == gcd a b <> c++checkLCM_reductivity_left (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen (a, a)) check+  where+    check a b = isJust (lcm a b </> a)++checkLCM_reductivity_right (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen (a, a)) check+  where+    check a b = isJust (lcm a b </> b)++checkLCM_uniqueness (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen (a, a, a)) check+  where+    check a b c =+        all isJust [c </> a, c </> b, lcm a b </> c] === (lcm a b == c)++checkLCM_idempotence (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen a) check+  where+    check a = lcm a a === a++checkLCM_identity_left (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen a) check+  where+    check a = lcm mempty a === a++checkLCM_identity_right (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen a) check+  where+    check a = lcm a mempty === a++checkLCM_commutativity (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen (a, a)) check+  where+    check a b = lcm a b === lcm b a++checkLCM_associativity (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen (a, a, a)) check+  where+    check a b c = lcm (lcm a b) c === lcm a (lcm b c)++checkLCM_absorption_gcd_lcm (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen (a, a)) check+  where+    check a b = lcm a (gcd a b) === a++checkLCM_absorption_lcm_gcd (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen (a, a)) check+  where+    check a b = gcd a (lcm a b) === a++checkLCM_distributivity_left (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen (a, a, a)) check+  where+    check a b c = lcm (a <> b) (a <> c) === a <> lcm b c++checkLCM_distributivity_right (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen (a, a, a)) check+  where+    check a b c = lcm (a <> c) (b <> c) === lcm a b <> c++checkLCM_distributivity_gcd_lcm (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen (a, a, a)) check+  where+    check a b c = lcm a (gcd b c) === gcd (lcm a b) (lcm a c)++checkLCM_distributivity_lcm_gcd (LCMMonoidInstance (_ :: a)) =+    forAll (arbitrary :: Gen (a, a, a)) check+  where+    check a b c = gcd a (lcm b c) === lcm (gcd a b) (gcd a c)  textualFactors :: TextualMonoid t => t -> [Either t Char] textualFactors = map characterize . factors
monoid-subclasses.cabal view
@@ -1,5 +1,5 @@ Name:                monoid-subclasses-Version:             1.2.1+Version:             1.2.2 Cabal-Version:       >= 1.10 Build-Type:          Simple Synopsis:            Subclasses of Monoid@@ -11,7 +11,7 @@    License:             BSD3 License-file:        BSD3-LICENSE.txt-Copyright:           (c) 2013-2021 Mario Blažević+Copyright:           (c) 2013-2023 Mario Blažević Author:              Mario Blažević Maintainer:          Mario Blažević <blamario@protonmail.com> Homepage:            https://github.com/blamario/monoid-subclasses/@@ -24,7 +24,7 @@ Library   hs-source-dirs:    src   Exposed-Modules:   Data.Semigroup.Cancellative, Data.Semigroup.Factorial,-                     Data.Monoid.Cancellative, Data.Monoid.GCD, Data.Monoid.Monus,+                     Data.Monoid.Cancellative, Data.Monoid.GCD, Data.Monoid.LCM, Data.Monoid.Monus,                      Data.Monoid.Factorial, Data.Monoid.Null, Data.Monoid.Textual,                      Data.Monoid.Instances.ByteString.UTF8, Data.Monoid.Instances.CharVector,                      Data.Monoid.Instances.Concat, Data.Monoid.Instances.Measured, Data.Monoid.Instances.Positioned,
+ src/Data/Monoid/LCM.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE Haskell2010, FlexibleInstances #-}++-- | This module defines the 'LCMMonoid' subclass of the 'Monoid' class.+--+-- The 'LCMMonoid' subclass adds the 'lcm' operation, which takes two monoidal+-- arguments and finds their /least common multiple/, or (more generally) the+-- least monoid from which either argument can be subtracted with the '</>'+-- operation.+--+-- The 'LCMMonoid' class is for Abelian, /i.e./, 'Commutative' monoids.+--+module Data.Monoid.LCM (+    LCMMonoid (..)+    )+where++import Prelude hiding (lcm, max)+import qualified Prelude++import Data.IntSet (IntSet)+import Data.Monoid (Dual (..), Product (..), Sum (..))+import Data.Monoid.GCD (GCDMonoid)+import Data.Set (Set)+import Numeric.Natural (Natural)+import qualified Data.IntSet as IntSet+import qualified Data.Set as Set++-- These imports are marked as redundant, but are actually required by haddock:+import Data.Maybe (isJust)+import Data.Semigroup.Cancellative (Reductive ((</>)))+import Data.Semigroup.Commutative (Commutative)++-- | Class of Abelian monoids that allow the /least common multiple/ to be+--   found for any two given values.+--+-- Operations must satisfy the following laws:+--+-- __/Reductivity/__+--+-- @+-- 'isJust' ('lcm' a b '</>' a)+-- @+-- @+-- 'isJust' ('lcm' a b '</>' b)+-- @+--+-- __/Uniqueness/__+--+-- @+-- 'all' 'isJust'+--     [ \   \   c '</>' a+--     , \   \   c '</>' b+--     , 'lcm' a b '</>' c+--     ]+-- ==>+--     ('lcm' a b '==' c)+-- @+--+-- __/Idempotence/__+--+-- @+-- 'lcm' a a '==' a+-- @+--+-- __/Identity/__+--+-- @+-- 'lcm' 'mempty' a '==' a+-- @+-- @+-- 'lcm' a 'mempty' '==' a+-- @+--+-- __/Commutativity/__+--+-- @+-- 'lcm' a b '==' 'lcm' b a+-- @+--+-- __/Associativity/__+--+-- @+-- 'lcm' ('lcm' a b) c '==' 'lcm' a ('lcm' b c)+-- @+--+-- __/Absorption/__+--+-- @+-- 'lcm' a ('gcd' a b) '==' a+-- @+-- @+-- 'gcd' a ('lcm' a b) '==' a+-- @+--+-- __/Distributivity/__+--+-- @+-- 'lcm' (a '<>' b) (a '<>' c) '==' a '<>' 'lcm' b c+-- @+-- @+-- 'lcm' (a '<>' c) (b '<>' c) '==' 'lcm' a b '<>' c+-- @+-- @+-- 'lcm' a ('gcd' b c) '==' 'gcd' ('lcm' a b) ('lcm' a c)+-- @+-- @+-- 'gcd' a ('lcm' b c) '==' 'lcm' ('gcd' a b) ('gcd' a c)+-- @+--+class GCDMonoid m => LCMMonoid m where+    lcm :: m -> m -> m++instance LCMMonoid () where+    lcm () () = ()++instance LCMMonoid a => LCMMonoid (Dual a) where+    lcm (Dual a) (Dual b) = Dual (lcm a b)++instance LCMMonoid (Product Natural) where+    lcm (Product a) (Product b) = Product (Prelude.lcm a b)++instance LCMMonoid (Sum Natural) where+    lcm (Sum a) (Sum b) = Sum (Prelude.max a b)++instance Ord a => LCMMonoid (Set a) where+    lcm = Set.union++instance LCMMonoid IntSet where+    lcm = IntSet.union++instance (LCMMonoid a, LCMMonoid b) => LCMMonoid (a, b) where+    lcm (a0, a1) (b0, b1) =+        (lcm a0 b0, lcm a1 b1)++instance (LCMMonoid a, LCMMonoid b, LCMMonoid c) => LCMMonoid (a, b, c) where+    lcm (a0, a1, a2) (b0, b1, b2) =+        (lcm a0 b0, lcm a1 b1, lcm a2 b2)++instance (LCMMonoid a, LCMMonoid b, LCMMonoid c, LCMMonoid d) =>+    LCMMonoid (a, b, c, d)+  where+    lcm (a0, a1, a2, a3) (b0, b1, b2, b3) =+        (lcm a0 b0, lcm a1 b1, lcm a2 b2, lcm a3 b3)