I’ve been wondering about the implementation of a library I’m working on. I kind of have a solution but would love comments or if this can be achieved differently.
So this is a media library. What I want users to be able to do is to create Records where there is one mandatory field and one optional field. The rest of the fields are ignored but they are passed down through components so these fields have to be stored so they can be raised as the same type again.
This was my initial idea
newtype Media r = Media
{ src :: String -- mandatory field
, thumbnail :: Maybe String -- optional field
| r -- rest of the fields
}
Now this is cool and all but it has some limitation. Because we are ignoring the fields but still keeping track of them this means I cannot actually mix together two “Media r” types, meaning if I have two different types such as
newtype Video = Video { id :: Int, name :: String, src :: String, thumbnail :: Maybe String }
newtype Image = Image { id :: Int, src :: String, thumbnail :: Maybe String }
the compiler will complain because the Image type does not contain the field name, although it does contain all the other fields.
My solution was the following:
Create the media type
newtype Media =
{ src :: String
, thumbnail :: Maybe String
, json :: Json
}
- Require users to make their type an instance of EncodeJson / DecodeJson
- Pass the type to/from a function encodeMedia/decodeMedia with the required constraints
- Encoding
- Encode the type to Json with encodeJson
- Use toObject to convert the Json to an Object so we get lookup
- Lookup the available keys (src, thumbnail)
- Assign the available keys to the Media type fields (src, thumbnail)
- Assign the original encoded Json to the Media json field
- Decoding
- Simply run decodeJson on the Media.json field. Since it has an instance of DecodeJson, that’s it
Pros
- I can now pass in any type that is encoded/decoded to/from { src :: String, thumbnail :: Maybe String } and the fields between types can vary
- I still have access to the actual Record so I could alter other fields (although now through Json)
Cons
- Might add a performance overhead (I have to do this to every img/video that is passed in)
- Errors are detected at runtime
It is my understanding that I cannot really do the same with records because as soon as I start picking out fields I need to restrict the function to those particular fields and then some polymorphic fields but of course it cannot be two different types of fields.
If anyone has comments on this approach I would love to hear them