-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | A safe interface for Const summarization -- -- A safe interface for using the Const functor to do -- summarization. See the Constable module for details. @package constable @version 0.1.0.0 -- | This module provides an interface for the Const functor which -- facilitates using it safely for summarization via the applicative -- instance Monoid m => Applicative (Const m). -- -- As an example, say we have a record of numbers and a function that -- sums up the fields of that record. We want to leverage the type system -- to ensure that if the record changes (field are added or removed), -- then the summing function will not compile until it is updated to -- match. -- -- As a first attempt, we might write something like this: -- --
--   import Data.Functor.Const
--   import Data.Monoid
--   
--   data Parts = MkParts
--     { part1 :: Int
--     , part2 :: Int
--     , part3 :: Int
--     }
--   
--   sumParts :: Parts -> Int
--   sumParts parts = getSum . getConst $
--     MkParts
--       <$> Const (Sum $ part1 parts)
--       <*> Const (Sum $ part2 parts)
--       <*> Const (Sum $ part3 parts)
--   
--   
-- -- At first glance it looks like this accomplishes our goal, but there is -- a serious bug. If we remove a field from the record then -- sumParts will indeed fail to compile, but if we add a field -- then it will compile without issue! This is because we didn't -- explicitly provide a type to the Const expression being -- constructed and so the compiler infers it to be a function type rather -- than the fully applied type that we expect. This means we must always -- remember to add explicit type signatures or type applications anywhere -- we use this pattern. -- -- If we instead use the getConstSafe version of getConst -- exported by this module then we don't need to worry about this issue -- because the compiler will complain if the inferred type of a -- Const is for a function type. -- -- If you use HLint, you can go one step further and ban the use of the -- "unsafe" getConst in your codebase by way of an HLint rule: -- --
--   - functions:
--     - {name: getConst, within: []}
--   
--   
module Constable -- | Unwraps Const. Throws a type error if that Const has a -- function type for its second argument. getConstSafe :: FullyAppliedConst b => Const a b -> a -- | Smart constructor for Const that enforces that the value it -- contains is built from a value of the result type. This provides an -- extra measure of type safety. -- --
--   data Summable = MkSummable
--     { s1 :: Int
--     , s2 :: Double
--     , s3 :: Integer
--     } deriving (Show)
--   
--   test :: Summable
--   test = Summable
--     { s1 = 1
--     , s2 = 2
--     , s3 = 3
--     }
--   
--   getConstSafe $ MkSummable
--     <$> mkConst (s1 test) (Sum . fromIntegral)
--     <*> mkConst (s2 test) Sum
--     <*> mkConst (s3 test) (Sum . fromIntegral)
--   
mkConst :: a -> (a -> m) -> Const m a -- | Smart constructor for Const like mkConst but where the -- argument is the result type applied to some type constructor -- f. mkConstF :: f a -> (f a -> m) -> Const m a -- | The Const functor. data Const a (b :: k) -- | A pattern synonym for Const that upholds the fully applied -- constructor invariant for its second type argument. pattern Const :: FullyAppliedConst b => a -> Const a b -- | A constraint enforcing that getConstSafe is only used with -- non-partially applied constructors. type family FullyAppliedConst (a :: Type) :: Constraint