Haxl中的代码重用 - 避免每个请求类型的GADT构造函数

时间:2018-01-14 17:22:23

标签: haskell yesod haxl

Haxl是一个了不起的库,但我发现的一个主要难点是由于对数据源的每种请求都需要在请求GADT中拥有自己的构造函数。例如,以tutorial

为例
data BlogRequest a where
  FetchPosts       :: BlogRequest [PostId]
  FetchPostContent :: PostId -> BlogRequest PostContent

然后,这些构造函数中的每一个都在模式匹配上并在DataSource实例的匹配函数中单独处理。这种风格为非平凡的应用程序带来了很多样板。例如,使用关系数据库的应用程序,其中每个表都有一个主键。可能有数百个表,所以我不想为每个表定义构造函数(更不用说跨表的所有可能的连接......)。我真正想要的是:

data DBRequest a where
  RequestById :: PersistEntity a => Key a -> DBRequest (Maybe a)

我使用persistent来从我的表中创建类型,但这不是一个关键细节 - 我只想使用单个构造函数来实现多种可能的返回类型。

尝试编写fetch函数时出现问题。 Haxl的常用过程是在构造函数上进行模式匹配,以分离各种类型的BlockedFetch请求,在上面的示例中,这些请求将对应于以下内容:

        resVars :: [ResultVar (Maybe a)]
        args :: [Key a]
        (psArgs, psResVars) = unzip
            [(key, r) | BlockedFetch (RequestById key) r <- blockedFetches]

...然后我会(以某种方式)按其键类型对参数进行分组,并为每个组分派SQL查询。但是这种方法不会因为这里可能是多个PersistentEntity类型(即数据库表)的请求,每个类型都是不同类型a,因此构建列表是不可能的。我曾想过使用存在量化类型来解决这个问题(类似于单例库中的SomeSing),但是我认为没有办法按需要对请求进行分组而不对每个可能的表/类型进行模式匹配。 / p>

有没有办法实现这种代码重用?

1 个答案:

答案 0 :(得分:1)

我看到两种方法:

mov eax, esp

Typeable

或GADT“tag”类型:

data DBRequest a where
  RequestById :: (Typeable a, PersistEntity a) => Key a -> DBRequest (Maybe a)

这些是非常相似的模式,特别是如果你使用GHC-8.2, 与https://hackage.haskell.org/package/base-4.10.1.0/docs/Type-Reflection.html (将data Tag a where TagValue1 :: Tag Value1 TagValue2 :: Tag Value2 TagValue3 :: Tag Value3 TagValue4 :: Tag Value4 TagValue5 :: Tag Value5 data DBRequest a where RequestById :: PersistEntity a => Tag a => Key a -> DBRequest (Maybe a) 替换为Tag a)。

无论哪种方式,您都可以使用标记对TypeRep a进行分组。我没试过,但是 Key a可能很方便:http://hackage.haskell.org/package/dependent-map