2

How to encode the following sum-type using Hasql? I tried to create an encoder sumValue :: Value SumValue, but I can't pattern match. E.g. I would like to encode SumBool with Hasql.Encoders.bool and SumChar with Hasql.Encoders.char.

data SumValue
  = SumBool !(Maybe Bool)
  | SumChar !(Maybe Char)
  | SumDate !(Maybe Day)
  | ...
  | SumUuid !(Maybe UUID)
  deriving (Eq, Show)
  • Why would you at all need that? It seems like you have some design problem. Even if you encode such a value, you're likely to get into trouble on the server side, because the server always expects a specific type. Can you elaborate on your use case? – Nikita Volkov May 23 '17 at 13:57
  • I'm building an application that can load CSV data from a file into a database table. The user assigns CSV fields to database columns and specifies the data conversion to use. Since the user specifies the data conversion, I don't know upfront what kind of data each value will be, hence the sum-type with one entry per data type. – Martijn Rijkeboer May 23 '17 at 17:02
  • 1
    Whenever you design database-driven application, the database should come first. If you approached your design that way, you would have discovered that it would have been impossible to create a schema, which supports what your aiming for (i.e., a column, which can hold any type). The only way to encode a sum-type in a relational database is using multiple columns, one per each type. I.e.: sum_bool, sum_char ... And then you wouldn't need the sum value encoder, as I expected. – Nikita Volkov May 24 '17 at 08:30
  • To clarify: the result of the conversion is a `Map Text SumType` where the `Text` is the name of the database column, so I'm not storing sum-types in the database. Furthermore, the user specifies both the database table layout and the conversion at run-time. – Martijn Rijkeboer May 25 '17 at 17:31

1 Answers1

2

While this seems highly likely to be a mistake in your design, in the current version of Hasql (0.19.*) it is possible to implement such an encoder at least on the level of Params. However I must notice that, unless provided with a use-case, which proves it useful, the support for this will be removed completely in the next major release in the sake of type-level security.

Either way, here's how you can do that now:

import qualified Hasql.Encoders as A

data SumValue
  = SumBool !(Maybe Bool)
  | SumChar !(Maybe Char)
  | SumDate !(Maybe Day)
  | SumUuid !(Maybe UUID)
  deriving (Eq, Show)

sumValue :: A.Params SumValue
sumValue =
  contramap match $
  chosen (A.nullableValue A.bool) $
  chosen (A.nullableValue A.char) $
  chosen (A.nullableValue A.date) $
  A.nullableValue A.uuid
  where
    match =
      \case
        SumBool x -> Left x
        SumChar x -> Right (Left x)
        SumDate x -> Right (Right (Left x))
        SumUuid x -> Right (Right (Right x))
Nikita Volkov
  • 42,792
  • 11
  • 94
  • 169