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')