Haskell存储具有持久定义的ADT

时间:2016-02-17 18:59:16

标签: database haskell functional-programming yesod

如何创建类似于此ADTs Yesod Persistent 实体:

import Data.Ratio

data Work = Work { name :: String
                 , payment :: WorkPayment
                 , duration :: WorkDuration }

data WorkPayment = WorkPaymentUndefined
                 | WorkPaymentEstimated { workPaymentEstimatedUnity :: TemporalUnity
                                        , workPaymentEstimatedValue :: Ratio Int }
                 | WorkPaymentTotal { workPaymentTotalValue :: Ratio Int }

data WorkDuration = WorkDurationUndefined
                  | WorkDurationEstimated { workDurationEstimatedUnity :: TemporalUnity
                                          , workDurationEstimatedQuantity :: Int }
                  | WorkDurationPermanent

data TemporalUnity = Hour | Day | Week | Month

使用分离的模块可以避免使用长名称,但我更喜欢将它们放在一起用于此示例。

对于简单的Enums来说,这看起来非常简单,但我无法弄清楚这种情况下的表格如同像MySQL这样的关系数据库

1 个答案:

答案 0 :(得分:1)

要使用Persistent存储ADT,您只需要告诉Persistent如何从数据库序列化/反序列化该值。为此,请将其设为PersistField的实例(将其转换为基本类型,如TextByteString)和PersistFieldSql(以指定数据库的类型)要使用的栏目。)

您可以选择(反)序列化数据。我想编码/解码到二进制值会更快,但我推荐JSON,因为:

  • 人类可以从数据库级别读取
  • 有些数据库内置了对它的支持
  • 便携式
  • 支持已通过to/fromPersistValueJSON
  • 内置到Persistent中

以下是一个例子:

-- Add this extension to derive To/FromJSON instances; see Data.Aeson for details
{-# LANGUAGE DeriveGeneric #-}

module Models.Work where

import Database.Persist.Sql (PersistFieldSql(..))
import ClassyPrelude.Yesod
import Data.Aeson

data TemporalUnity = Hour | Day | Week | Month deriving (Generic)

instance FromJSON TemporalUnity
instance ToJSON TemporalUnity

data WorkDuration = WorkDurationUndefined
                  | WorkDurationEstimated { workDurationEstimatedUnity :: TemporalUnity
                                          , workDurationEstimatedQuantity :: Int }
                  | WorkDurationPermanent deriving (Generic)

instance FromJSON WorkDuration
instance ToJSON WorkDuration

-- Store the values as as SqlString. The actual column type will be dependent on the database backend. SqlString will be something like varchar/text types.
-- You can also use PersistByteString, which might be faster
instance PersistFieldSql WorkDuration where
  sqlType _ = SqlString


instance PersistField WorkDuration where
  toPersistValue = toPersistValueJSON
  fromPersistValue = fromPersistValueJSON

以下是从Postgres中选择的内容:

foo=# SELECT * FROM "user";
 id | ident  | password |                                               duration
----+--------+----------+-------------------------------------------------------------------------------------------------------
  5 | foo123 |          | {"tag":"WorkDurationPermanent","contents":[]}
  6 | bar456 |          | {"tag":"WorkDurationEstimated","workDurationEstimatedQuantity":1,"workDurationEstimatedUnity":"Hour"}

我不太熟悉MySQL或Postgres中的原生JSON支持,但如果您想根据基础JSON数据进行查询,可能需要调查一下。