Hi I'm trying to get how I would collect the same attibute of a (possibly) deeply nested data structure. The following data types are
data PageContent =
PageElement { content_type :: String
, element :: String
, attributes :: Maybe Object
, services :: Maybe [ServiceRequest]
, body :: Maybe [PageContent]
, styles :: Maybe Object
, scripts :: Maybe Object }
| PageComponent { content_type :: String
, id :: String
, tag :: Maybe String
, args :: Maybe Object
, services ::Maybe [ServiceRequest]}
| PageConditional { content_type :: String
, true :: Maybe [PageContent]
, false :: Maybe [PageContent]
, condition :: String }
deriving(Show)
So in my case I'm trying to collect all ServiceRequest that are declared. Elements are passed in as a list of [PageContent]. I think it would be perfect to use a recursive function because for example when I hit a PageElement that element could have a _body which has a Maybe [PageContent], so going through the _body of a PageElement should be identical to going through the original [PageContent] list passed in, given that I extract it from Maybe.
I have a hard time figuring out how I would return this as a new list
getPageContentServiceRequests :: [PageContent] -> Maybe [ServiceRequest]
So I could have no ServiceRequest or I could have a list of 1 or more ServiceRequests. If I do this recursively, would I pass two lists to the function? One with [PageContent] and then an empty list which I could add to recursively while collecting ServiceRequests?
example
getPageContentServiceRequests (x:xs) (y:ys) =
case (isJust (body x)) of
True -> y : getPageContentServiceRequests fromJust (body x) ys
Would this be the correct approach? I feel I understand the recursive concept when I'm working with a list of the same type but not if I have to construct a completely new list with a different type.
Maybe a
is a Monoid
as long as a
is a Monoid
:
instance Monoid a => Monoid (Maybe a)
Therefore, we can map
each PageContent
to a Maybe [ServiceRequest]
and mconcat
them.
The PageElement
has a services :: Maybe [ServiceRequest]
, but it may also have more Maybe [ServiceRequest]
s under the body :: Maybe [PageContent]
. We can use getPageContentServiceRequests
on the body, using >>=
to join the two levels of Maybe
that would be introduced, and use <>
(aka mappend
) to append them with services
. We can do the same for PageComponent
and PageConditional
:
import Data.Monoid
getPageContentServiceRequests :: [PageContent] -> Maybe [ServiceRequest]
getPageContentServiceRequests = mconcat . map getServiceRequests where
getServiceRequests :: PageContent -> Maybe [ServiceRequest]
getServiceRequests PageElement { services=services, body=body } =
services <> (body >>= getPageContentServiceRequests)
getServiceRequests PageComponent { services=services } =
services
getServiceRequests PageConditional { true=true, false=false } =
(true >>= getPageContentServiceRequests) <>
(false >>= getPageContentServiceRequests)