I have a function like so:
test :: [Block] -> [Block]
test ((Para foobar):rest) = [Para [(foobar !! 0)]] ++ rest
test ((Plain foobar):rest) = [Plain [(foobar !! 0)]] ++ rest
Block
is a datatype that includes Para
, Plain
and others.
What the function does is not particularly important, but I notice that the body of the function ([Para [(foobar !! 0)]] ++ rest
) is the same for both Para
and Plain
, except that the constructor used is the type of foobar
.
Question: is there someway to concisely write this function with the two cases combined? Something like
test :: [Block] -> [Block]
test ((ParaOrPlain foobar):rest) = [ParaOrPlain [(foobar !! 0)]] ++ rest
where the first ParaOrPlain
matches either Para foobar
or Plain foobar
and the second ParaOrPlain
is Para
or Plain
respectively.
Note that Block
can also be (say) a BulletList
or OrderedList
and I don't want to operate on those. (edit: test x = x
for these other types.)
The key is that I don't want to replicate the function body twice since they're identical (barring the call to Para
or Plain
).
I get the feeling I could use Either
, or perhaps my own data type, but I'm not sure how to do it.
Edit To clarify, I know my function body is cumbersome (I'm new to Haskell) and I thank various answerers for their simplifications.
However at the heart of the problem, I wish to avoid the replication of the Para
and Plain
line. Something like (in my madeup language...)
# assume we have `test (x:rest)` where `x` is an arbirtrary type
if (class(x) == 'Para' or class(x) == 'Plain') {
conFun = Plain
if (class(x) == 'Para')
conFun = Para
return [conFun ...]
}
# else do nothing (ID function, test x = x)
return x:rest
i.e., I want to know if it's possible in Haskell to assign a constructor function based on the type of the input parameter in this way. Hope that clarifies the question.
One approach is to use a (polymorphic) helper function, e.g.:
helper ctor xs rest = ctor [ head xs ] : rest
test :: [Block] -> [Block]
test ((Para xs):rest) = helper Para xs rest
test ((Plain xs):rest) = helper Plain xs rest
test bs = bs
Note that Para
and Plain
are just functions of type [whatever] -> Block
.
Assuming a data type declaration in the form:
data Block
= Para { field :: [Something] }
| Plain { field :: [Something] }
...
You can simply use a generic record syntax:
test :: [Block] -> [Block]
test (x:rest) = x { field = [(field x) !! 0] } : rest
Live demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With