Suppose I would like to write a bunch of functions, say func0 to func9. They take the same input but do different things with them. However, all of them involve the same input check. For example:
func0 :: Int -> [a] -> Either String a
func0 i lst
| i < 0 || i > length lst = Left "Index Out of Bounds!"
| otherwise = -- does things
The part that checks the index out of bounds is identical for each function, so I would like to not repeat them for each function. Is there any do wizardry to clean the code up a little bit? Thanks in advance!
CodePudding user response:
This seems pretty straightforward to factor out into a higher-order function:
check :: (Int -> [a] -> Either String a) -> Int -> [a] -> Either String a
check f i as
| i < 0 || i >= length as = Left "Index Out of Bounds!"
| otherwise = f i as
func0 = check myFunc0
func1 = check myFunc1
and so on, where myFunc0 etc are the parts of the functions which presumably have different behaviour.
(It's not clear what they do, if they all just give a Right value then you probably want to alter them so they simply return an a and put the wrapping in Right in the otherwise clause of check. But the above allows you to return a Left failure value in other circumstances if you need to.)
(I've also changed i > length as to i >= length as because i = length as will also cause a crash assuming you're going to use i as an index.)
CodePudding user response:
I would put the bounds check in the indexing function. Like this:
(!?) :: [a] -> Int -> Either String a
lst !? i = case drop i lst of
_ | i < 0 -> bad
[] -> bad
a:_ -> Right a
where bad = Left "Index Out of Bounds!"
Now you can write func0 and friends in terms of it without checking the index first, as in:
func0 :: Num a => Int -> [a] -> Either String a
func0 i lst = do
a <- lst !? (i-1)
a' <- lst !? (i 1)
return (a a')
