在 Haskell 中创建 Semigroup 数据类型实例
Creating a Semigroup data type instance in Haskell
可能的目标是为 Haskell 中新定义的数据类型创建一个新的 Semigroup 类型 class 实例(对于那些知道 "Get programming with Haskell" book by Will Kurt 的人,我可能会向您推荐第 428 页,即带有练习扩展的顶点项目 5 的结尾)。
有一个新定义的数据类型:
data HINQ m a b = HINQ (m a -> m b) (m a) (m a -> m a)
| HINQ_ (m a -> m b) (m a)
此数据类型指定类似SQL的查询,其中m
定义上下文(Monad或Alternative),(m a -> m b)
是目标类似于[=39=的函数] 函数 SELECT
,即定义了人们希望在数据库中看到的 属性 类型,(m a)
是一个应用前一个函数的“table”(类似于 SQL 的 table_name) 最后 (m a -> m a)
过滤掉正在寻找的 属性 (类似于 SQL 的 WHERE
)。
我的目标是使此数据类型成为半群(最后是幺半群)的实例。值得一提的是,假定 a
、b
等所需的所有 Semigroup 实例。
instance (Semigroup a, Semigroup (m a), Semigroup b,...) =>
Semigroup (HINQ m a b) where
(<>) (HINQ func1 start1 test1)
(HINQ func2 start2 test2) =
所以它的粗略想法(在背景上看得更清楚)是可以将对数据库的几个不同查询组合成一个查询,但我想不出如何同时合并两个 (m a -> m b)
类型的不同函数合并两个 tables (m a)
...第一个想法是将它们组合成列表,但随后类型签名发生了变化我还没有还没找到解决办法。
我想你不想要 Semigroup
。组合 all 对查询是没有意义的——只有其中一个的输出类型与另一个的输入类型合理匹配的查询!幸运的是,我们有一个概念对应于 Semigroup
的“类型化”变体(实际上是类型化的 Monoid
,但足够接近):Category
.
此外,我认为将查询与您正在查询的 table 相结合是一个设计错误。它们在概念上是独立的概念;实际上,在组合两个查询时,您仍然只有一个 table,而不是两个。所以:
data HINQ m a b = HINQ (m a -> m b) (m a -> m a)
instance Category (HINQ m) where
id = HINQ id id
HINQ slct whr . HINQ slct' whr' = HINQ (slct . whr . slct') whr'
恒等律很清楚,但是左右WHERE
子句用法的不对称看起来有点可疑,所以我们应该仔细检查结合律:
(HINQ s0 w0 . HINQ s1 w1) . HINQ s2 w2
= HINQ (s0 . w0 . s1) w1 . HINQ s2 w2
= HINQ (s0 . w0 . s1 . w1 . s2) w2
= HINQ s0 w0 . HINQ (s1 . w1 . s2) w2
= HINQ s0 w0 . (HINQ s1 w1 . HINQ s2 w2)
看起来不错!
编辑
呃,嗯...也许 x . id = x
法律毕竟不是那么明确。哎呀!这可能无法修复,除非您只考虑包含函数的组合的相等性,在这种情况下,为什么不直接对函数使用 Category
实例呢?当然,您还有另一种选择,即不要求保持身份法则。这有点不寻常,但我想这是否合理在很大程度上取决于您的用例。
如果你更明确地表示你的过滤,组合可能会更容易,例如作为 a -> Bool
或 a -> m Bool
而不是 m a -> m a
。这使您有更多机会在 (.)
实现中组合两个过滤器,而不是像上面的实例那样将其中一个过滤器滚动到 select 操作中。
可能的目标是为 Haskell 中新定义的数据类型创建一个新的 Semigroup 类型 class 实例(对于那些知道 "Get programming with Haskell" book by Will Kurt 的人,我可能会向您推荐第 428 页,即带有练习扩展的顶点项目 5 的结尾)。
有一个新定义的数据类型:
data HINQ m a b = HINQ (m a -> m b) (m a) (m a -> m a)
| HINQ_ (m a -> m b) (m a)
此数据类型指定类似SQL的查询,其中m
定义上下文(Monad或Alternative),(m a -> m b)
是目标类似于[=39=的函数] 函数 SELECT
,即定义了人们希望在数据库中看到的 属性 类型,(m a)
是一个应用前一个函数的“table”(类似于 SQL 的 table_name) 最后 (m a -> m a)
过滤掉正在寻找的 属性 (类似于 SQL 的 WHERE
)。
我的目标是使此数据类型成为半群(最后是幺半群)的实例。值得一提的是,假定 a
、b
等所需的所有 Semigroup 实例。
instance (Semigroup a, Semigroup (m a), Semigroup b,...) =>
Semigroup (HINQ m a b) where
(<>) (HINQ func1 start1 test1)
(HINQ func2 start2 test2) =
所以它的粗略想法(在背景上看得更清楚)是可以将对数据库的几个不同查询组合成一个查询,但我想不出如何同时合并两个 (m a -> m b)
类型的不同函数合并两个 tables (m a)
...第一个想法是将它们组合成列表,但随后类型签名发生了变化我还没有还没找到解决办法。
我想你不想要 Semigroup
。组合 all 对查询是没有意义的——只有其中一个的输出类型与另一个的输入类型合理匹配的查询!幸运的是,我们有一个概念对应于 Semigroup
的“类型化”变体(实际上是类型化的 Monoid
,但足够接近):Category
.
此外,我认为将查询与您正在查询的 table 相结合是一个设计错误。它们在概念上是独立的概念;实际上,在组合两个查询时,您仍然只有一个 table,而不是两个。所以:
data HINQ m a b = HINQ (m a -> m b) (m a -> m a)
instance Category (HINQ m) where
id = HINQ id id
HINQ slct whr . HINQ slct' whr' = HINQ (slct . whr . slct') whr'
恒等律很清楚,但是左右WHERE
子句用法的不对称看起来有点可疑,所以我们应该仔细检查结合律:
(HINQ s0 w0 . HINQ s1 w1) . HINQ s2 w2
= HINQ (s0 . w0 . s1) w1 . HINQ s2 w2
= HINQ (s0 . w0 . s1 . w1 . s2) w2
= HINQ s0 w0 . HINQ (s1 . w1 . s2) w2
= HINQ s0 w0 . (HINQ s1 w1 . HINQ s2 w2)
看起来不错!
编辑
呃,嗯...也许 x . id = x
法律毕竟不是那么明确。哎呀!这可能无法修复,除非您只考虑包含函数的组合的相等性,在这种情况下,为什么不直接对函数使用 Category
实例呢?当然,您还有另一种选择,即不要求保持身份法则。这有点不寻常,但我想这是否合理在很大程度上取决于您的用例。
如果你更明确地表示你的过滤,组合可能会更容易,例如作为 a -> Bool
或 a -> m Bool
而不是 m a -> m a
。这使您有更多机会在 (.)
实现中组合两个过滤器,而不是像上面的实例那样将其中一个过滤器滚动到 select 操作中。