1

I have the following model:

User
  ...
Group
  ...
Sharing
  objectId (Either UserId GroupId)

In Sharing entity I want to store either UserId or GroupId and differentiate between them. Simply using Either doesn't work:

  • Not in scope: type constructor or class `UserId'
  • Not in scope: type constructor or class `GroupId'

Adding a new sum-type also doesn't work:

data SharingIdType = SharingUserId UserId | SharingGroupId GroupId
  • Not in scope: type constructor or class `SharingIdType'

Moving SharingIdType into another module isn't possible, because it uses UserId and GroupId types. The only way I see is to create an entity for each sharing type, like UserSharing/GroupSharing.

Other than that, how to approach this problem?

Chris Stryczynski
  • 30,145
  • 48
  • 175
  • 286
arrowd
  • 33,231
  • 8
  • 79
  • 110
  • 1
    I have no experience with persistent, but I think underlying databases do not support sum types, so persistent can not map them (?). However, `Maybe` types should be mappable to nullable types, I guess. – chi Mar 25 '18 at 20:30
  • Under the hood both `UserId` and `GroupId` are `Key a`, which, in turn, is just `Int64` for SQL backends. So it should be possible. I even OK with dropping some type safety. – arrowd Mar 25 '18 at 20:32
  • 3
    It is not possible to represent `Either Int64 Int64` inside `Int64`, since the former contains 1 more bit of information ("left" or "right"). – chi Mar 25 '18 at 20:46
  • 2
    To expand @chi 's point further - Sure, you can serialize. But how do you deserialize? How do you know which type of key it is? – Carl Mar 25 '18 at 21:48
  • Can you get away with sharing with a User implicitly creating a single-user group? (Or even always create a canonical single user group for each user). It just moves the awkwardness to a different place, so it might not be appropriate, but the different place is probably in Haskell land rather than persistent's DB DSL, so you'd have more tools available. – Ben Mar 26 '18 at 01:38
  • 1
    @Carl I was hoping persistent would hide from the details. It could've add additional column to store the constructor for sum type, for instance. But I guess, I demand too much. – arrowd Mar 28 '18 at 05:58
  • @arrowd, that surprises me a bit too. It's especially strange to me that there's an instance for lists but not for `Either`. The latter doesn't really seem inherently harder. Maybe you should open a GitHub issue for that? – dfeuer May 21 '18 at 22:09

1 Answers1

0

After searching for some time and thinking about it I concluded there are two possible solutions:

1.

If number of SharingIdTypes is static or rarely changes (means, it is OK to recompile the source to change it or alter the DB schema), the proper way to handle the problem is to have to entities for each sharing type:

User
  ...
Group
  ...
UserSharing
  userId UserId
GroupSharing
  groupId GroupId

Here the "sumness" of the problem is moved to DB queries. Whenever I need to find out with what something shared, I make two selectLists and query two tables instead of one.

2.

If number of SharingIdTypes needs to be altered dynamically, the SharingType entity is needed:

User
  ...
Group
  ...
SharingType
  description String
Sharing
  objectId SharingTypeId

This table is filled up with values corresponding to SharingIdTypes constructors:

do
  insert $ SharingType "user"
  insert $ SharingType "group"

Now whenever we share something, we refer SharingTypeId.

arrowd
  • 33,231
  • 8
  • 79
  • 110