I'm wondering if it's possible to get all fields of a record in Haskell that end with a certain name. For example
data Record = Record {
field :: String
field2_ids :: Maybe [Int]
field3_ids :: Maybe [Int]
}
In this case I would like to get a list of fields ending with "ids". I don't know their names. I only know that they end with "ids" What I need is both the field name and the value it contains. So I guess this would be a list of maps
[{field2_ids = Maybe [Int]}, {fields3_ids = Maybe [Int]}...]
or even a list of tuples
[("field2_ids", Maybe [Int])...]
By the way, in my case the fields I'm extracting will always have the type of Maybe [Int]
.
Is this possible? I suspect that it's not possibly with vanilla record syntax but is this maybe something that could be achieved with lenses?
UPDATE
I understand my question is causing some confusion in terms of what I'm actually trying to do. So I will explain
I'm using a microservice pattern with Services. Each Service is tied to a single data model. For example a Blog Service would contain a single Blog model. But the Blog Service can have all kinds of relations. For example it can have a relation to a Category Service. It can also have a relation to a Tag Service. Since there is a possibility of having more than one relation with another Service I have the type of a Maybe [Int]
since I could be posting a Blog with Just [Int]
or Nothing
, no relations at all. Each Service handles its relations by registering them in a Relation table.
So to create a new Blog Post I need a data structure like this one in Servant
data BlogPostRequest = BlogPostRequest {
title :: String,
published :: DateTime,
public :: Bool,
category_ids :: Maybe [Int],
tag_ids :: Maybe [Int]
}
The endpoint will take all the fields related to the Blog model and store it as a new Blog instance. It will then take all the relations if present in category_ids and tag_ids and store it in the Relation table.
My only concern about this, using the traditional record syntax is that if I have multiple relations, the code will get very bloated. Services are generated from config files. So yes I do actually know all the names of the fields from start. I'm sorry my statement about this before was very confusing. My point was that if I could pull the fields out of the record just by knowing that their names end with _ids I could reduce a lot of the code.
This would be the vanilla record syntax approach. Imagine that storeRelation is a method which takes a String
and a Maybe [Int]
and handles storing the relation accordingly
createNewBlogPost post =
storeRelation "category" (category_ids post)
storeRelation "tag" (tag_ids post)
-- continue with rest of relations
This approach might not be so bad in the end. I would just add a new line for each relation. I was just wondering if there was a straight forward way to extract fields from records so that I could have the function like this
createNewBlogPost post =
storRelation $ extractRelations post
where storeRelation now takes a list of tuples and extractRelations is a function which extracts the fields ending with _ids
Given that you actually do know all of the field names, and they are all of the same type, it should be quite a small amount of work to simply write each of the field names once, and much simpler than writing a big generic Template Haskell solution that would work for any data type.
A simple example:
idGetters :: [(String, Record -> Maybe [Int])]
idGetters = [("field2_ids", field2_ids),
("field3_ids", field3_ids)]
ids :: Record -> [(String, Maybe [Int])]
ids r = fmap (fmap ($ r)) idGetters
It looks a bit ugly, but that's simply the best way to work with the data structure you're presupposing.