Haskell 光束代码仅在一个模块中有效

Haskell Beam code only works in one module

我在这样的 Schema 模块中定义了 DatasourceId

import qualified Database.Beam as B

data DatasourceT f = Datasource
  { _datasourceId :: B.C f Text 
  , _datasourceName :: B.C f Text 
  } deriving (Generic, B.Beamable)

instance B.Table DatasourceT where
  data PrimaryKey DatasourceT f = DatasourceId (B.C f Text) deriving (Generic, B.Beamable)
  primaryKey = DatasourceId . _datasourceId

type DatasourceId = B.PrimaryKey DatasourceT Identity

但我无法像

那样在 Schema 模块之外使用它
(B.val_ $ DatasourceId $ _datasourceId d)

因为编译器抱怨

>     • Data constructor not in scope:
>         DatasourceId :: t0 -> B.PrimaryKey DatasourceT Identity
>     • Perhaps you meant 'Datasource' (imported from Schema)
>     |
> 229 |  (B.val_ $ DatasourceId $ _datasourceId d)

虽然我有

import Schema (ControlAccessDb(..), Datasource, DatasourceId, DatasourceT(..))

那么有没有什么建议可以防止代码注定要被合并到一个模块中呢?这可能是类型家族的怪癖吗? 看来问题出在

instance B.Table DatasourceT where
  data PrimaryKey DatasourceT f = DatasourceId (B.C f Text) deriving (Generic, B.Beamable)
  primaryKey = DatasourceId . _datasourceId

只有定义模块才知道实例化内部结构。

请注意,Beam 文档本身就有这样的用法(代码嵌入在文档中)。参见 https://tathougies.github.io/beam/user-guide/manipulation/insert/ where CustomerId is defined at https://github.com/tathougies/beam/blob/d87120b58373df53f075d92ce12037a98ca709ab/beam-sqlite/examples/Chinook/Schema.hs#L119

这是一种有点不直观的情况,会在任何使用附加类型的地方发生,而不是特定于 Beam。

因为 DatasouceId 是附加类型 PrimaryKey 的构造函数,您需要导出和导入该类型,就像处理普通的非附加类型一样。这有点不直观,因为 PrimaryKey 最初并未在您的模块中定义。但从某种意义上说,它是:您正在定义该类型的实例,因此您也可以导出它。我想这一定是这背后有争议的逻辑。

像这样从您的 Schema 模块中导出:

module Schema( ..., B.PrimaryKey(..), ... ) where

像这样在任何需要的地方导入:

import Schema(PrimaryKey(..))