Below is my solution for the CTFP chapter 4 challenges which essentially involves composing partial functions (that don't have defined outputs for all possible inputs i.e. returning a Maybe).
The challenge is to implement composition, identity(to satisfy category requirements) and try it out with 2 partial functions. As I'm relatively new to Haskell, I was hoping to get a general code review with suggestions on better idioms and testing I could follow. Link to Code
import Test.HUnit module Main where -- Given safeRoot :: Double -> Maybe Double safeRoot x | x < 0 = Nothing | otherwise = Just (sqrt x) -- Q1: Identity and composition partialFnId :: a -> Maybe a partialFnId x = Just x partialFnCompose :: (b -> Maybe c) -> (a -> Maybe b) -> (a -> Maybe c) partialFnCompose g f = \x -> case (f x) of Just value -> g value Nothing -> Nothing -- Testing Q1 testPartialFnMeetsCategoryRequirements1 = TestCase $ assertEqual "Id should pass through inputs" (Just 3) (partialFnId 3) testPartialFnMeetsCategoryRequirements2 = TestCase $ assertEqual "Composition with identity is noop(1)" (Just 2.0) ((partialFnCompose partialFnId safeRoot) 4) testPartialFnMeetsCategoryRequirements3 = TestCase $ assertEqual "Composition with identity is noop(2)" (Just 2.0) ((partialFnCompose safeRoot partialFnId) 4) -- Q2: Implement safeReciprocal safeReciprocal :: Double -> Maybe Double safeReciprocal x | x == 0 = Nothing | otherwise = Just (1 / x) -- Q3: Implement safeRootReciprocal via composing the above 2. safeRootReciprocal :: Double -> Maybe Double safeRootReciprocal = partialFnCompose safeRoot safeReciprocal -- Testing Q3 testSafeRootReciprocal1 = TestCase $ assertEqual "Provides root of reciprocal for valid inputs" (Just 2.0) (safeRootReciprocal 0.25) testSafeRootReciprocal2 = TestCase $ assertEqual "Provides Nothing on invalid inputs(1)" Nothing (safeRootReciprocal 0.0) testSafeRootReciprocal3 = TestCase $ assertEqual "Provides Nothing on invalid inputs(2)" Nothing (safeRootReciprocal (-0.25)) main = do runTestTT $ TestList [testPartialFnMeetsCategoryRequirements1, testPartialFnMeetsCategoryRequirements2, testPartialFnMeetsCategoryRequirements3, testSafeRootReciprocal1, testSafeRootReciprocal2, testSafeRootReciprocal3]