diff --git a/strict-base-types/src/Data/Maybe/Strict.hs b/strict-base-types/src/Data/Maybe/Strict.hs index f26225c..b30ebae 100644 --- a/strict-base-types/src/Data/Maybe/Strict.hs +++ b/strict-base-types/src/Data/Maybe/Strict.hs @@ -19,7 +19,10 @@ -- corresponding variants of the functions from "Data.Maybe". -- -- Note that in contrast to the standard lazy 'L.Maybe' type, the strict --- 'Maybe' type is not an applicative functor, and therefore also not a monad. +-- 'Maybe' type does not have lawful 'Functor', 'Applicative', or 'Monad' instances. +-- However, we provide instances for these type classes (and related others) +-- since they are only partial due to the below example. +-- -- The problem is the /homomorphism/ law, which states that -- -- @'pure' f '<*>' 'pure' x = 'pure' (f x) -- must hold for all f@ @@ -28,6 +31,8 @@ -- 'Maybe', as this instance does not satisfy @pure f \<*\> pure _|_ = pure (f -- _|_)@ for @f = const@. -- +-- This exception to the law is acceptable because we would expect a strict type to diverge on _|_ . +-- ----------------------------------------------------------------------------- module Data.Maybe.Strict ( diff --git a/strict/src/Data/Strict/Either.hs b/strict/src/Data/Strict/Either.hs index fcdcf86..eb60afa 100644 --- a/strict/src/Data/Strict/Either.hs +++ b/strict/src/Data/Strict/Either.hs @@ -21,9 +21,10 @@ -- The strict variant of the standard Haskell 'L.Either' type and the -- corresponding variants of the functions from "Data.Either". -- --- Note that the strict 'Either' type is not an applicative functor, and +-- Note that the strict 'Either' type is not technically an applicative functor, and -- therefore also no monad. The reasons are the same as the ones for the --- strict @Maybe@ type, which are explained in "Data.Maybe.Strict". +-- strict @Maybe@ type, which are explained in "Data.Maybe.Strict", but we define +-- instances for the same reasons listed as well. -- ----------------------------------------------------------------------------- @@ -38,8 +39,9 @@ module Data.Strict.Either ( -- import parts explicitly, helps with compatibility import Prelude ( Functor (..), Eq (..), Ord (..), Show (..), Read (..), Bool (..), (.), ($) - , error, Ordering (..), showParen, showString, lex, return, readParen) -import Control.Applicative (pure, (<$>)) + , error, Ordering (..), showParen, showString, lex, readParen) +import Control.Applicative (Applicative (..), (<$>)) +import Control.Monad (Monad (..)) import Data.Semigroup (Semigroup (..)) import Data.Foldable (Foldable (..)) import Data.Traversable (Traversable (..)) @@ -162,6 +164,21 @@ instance Semigroup (Either a b) where Left _ <> b = b a <> _ = a +-- | Unlawful when given `Right _|_`, due to the homomorphism law +instance Applicative (Either e) where + pure = Right + + Right f <*> m = fmap f m + Left l <*> _m = Left l + +instance Monad (Either e) where + (Right x) >>= k = k x + Left e >>= _ = Left e + + (>>) = (*>) + + return = pure + -- deepseq instance (NFData a, NFData b) => NFData (Either a b) where rnf = rnf . toLazy diff --git a/strict/src/Data/Strict/Maybe.hs b/strict/src/Data/Strict/Maybe.hs index c50059f..97b8555 100644 --- a/strict/src/Data/Strict/Maybe.hs +++ b/strict/src/Data/Strict/Maybe.hs @@ -22,7 +22,10 @@ -- corresponding variants of the functions from "Data.Maybe". -- -- Note that in contrast to the standard lazy 'L.Maybe' type, the strict --- 'Maybe' type is not an applicative functor, and therefore also not a monad. +-- 'Maybe' type does not have lawful 'Functor', 'Applicative', or 'Monad' instances. +-- However, we provide instances for these type classes (and related others) +-- since they are only partial due to the below example. +-- -- The problem is the /homomorphism/ law, which states that -- -- @'pure' f '<*>' 'pure' x = 'pure' (f x) -- must hold for all f@ @@ -31,6 +34,8 @@ -- 'Maybe', as this instance does not satisfy @pure f \<*\> pure _|_ = pure (f -- _|_)@ for @f = const@. -- +-- This exception to the law is acceptable because we would expect a strict type to diverge on _|_ . +-- ----------------------------------------------------------------------------- module Data.Strict.Maybe ( @@ -48,8 +53,9 @@ module Data.Strict.Maybe ( -- import parts explicitly, helps with compatibility import Prelude (Functor (..), Eq (..), Ord (..), Show (..), Read (..), Bool (..), (.) - ,error, Ordering (..), ($), showString, showParen, return, lex, readParen) -import Control.Applicative (pure, (<$>)) + ,error, Ordering (..), ($), showString, showParen, lex, readParen) +import Control.Applicative (Applicative (..), Alternative (..), (<$>)) +import Control.Monad (Monad (..), MonadPlus (..)) import Data.Monoid (Monoid (..)) import Data.Semigroup (Semigroup (..)) import Data.Foldable (Foldable (..)) @@ -181,6 +187,37 @@ instance Traversable Maybe where traverse _ Nothing = pure Nothing traverse f (Just x) = Just <$> f x +-- | Unlawful when given `Just _|_`, due to the homomorphism law +instance Applicative Maybe where + -- thanks to https://hackage.haskell.org/package/base-4.19.1.0/docs/src/GHC.Base.html#line-1161 + pure = Just + + Just f <*> m = fmap f m + Nothing <*> _m = Nothing + + liftA2 f (Just x) (Just y) = Just (f x y) + liftA2 _ _ _ = Nothing + + Just _m1 *> m2 = m2 + Nothing *> _m2 = Nothing + +instance Monad Maybe where + -- thanks to https://hackage.haskell.org/package/base-4.19.1.0/docs/src/GHC.Base.html#line-1161 + (Just x) >>= k = k x + Nothing >>= _ = Nothing + + (>>) = (*>) + + return = pure + +instance Alternative Maybe where + -- thanks to https://hackage.haskell.org/package/base-4.19.1.0/docs/src/GHC.Base.html#line-1161 + empty = Nothing + Nothing <|> r = r + l <|> _ = l + +instance MonadPlus Maybe + -- deepseq instance NFData a => NFData (Maybe a) where rnf = rnf . toLazy diff --git a/strict/src/Data/Strict/Tuple.hs b/strict/src/Data/Strict/Tuple.hs index 49a69ea..809b429 100644 --- a/strict/src/Data/Strict/Tuple.hs +++ b/strict/src/Data/Strict/Tuple.hs @@ -48,8 +48,9 @@ module Data.Strict.Tuple ( -- import parts explicitly, helps with compatibility import Prelude (Functor (..), Eq (..), Ord (..), Show (..), Read (..), (.), Bounded, map, ($) - , (&&), showParen, showString, readParen, lex, return) -import Control.Applicative ((<$>), (<*>)) + , (&&), showParen, showString, readParen, lex) +import Control.Applicative (Applicative (..), (<$>), (<*>)) +import Control.Monad (Monad (..)) import Data.Monoid (Monoid (..)) import Data.Semigroup (Semigroup (..)) import Data.Foldable (Foldable (..)) @@ -170,6 +171,16 @@ instance (Monoid a, Monoid b) => Monoid (Pair a b) where mempty = mempty :!: mempty (x1 :!: y1) `mappend` (x2 :!: y2) = (x1 `mappend` x2) :!: (y1 `mappend` y2) +instance Monoid l => Applicative (Pair l) where + pure = (mempty :!:) + (u :!: f) <*> (v :!: x) = (u <> v) :!: f x + liftA2 f (u :!: x) (v :!: y) = (u <> v) :!: f x y + +instance Monoid l => Monad (Pair l) where + (u :!: a) >>= k = case k a of (v :!: b) -> (u <> v) :!: b + + return = pure + -- deepseq instance (NFData a, NFData b) => NFData (Pair a b) where rnf = rnf . toLazy