【发布时间】:2011-12-08 17:12:18
【问题描述】:
我正在尝试在 Haskell 中实现一个 Redis 客户端库,我的目标是尽可能多地对 Haskell 类型系统中的 Redis 命令的语义进行编码。 Redis,对于那些不知道的人来说,是一个数据存储,可以通过网络访问。我会用它来举例说明我的问题,但是Redis不是这个问题的重点。
一个示例函数
考虑函数
get :: (RedisValue a) => Key -> Redis a
get k = decodeValue <$> sendCommand ["GET", key]
它将命令发送到数据存储并返回存储在给定Key 下的值(对于本示例,您可以考虑type Key = String)。至于返回类型:
Redis是Monad和MonadIO的一个实例。它封装了有关网络连接的信息。sendCommand发送请求并返回数据存储区的回复。a是多态的,例如可以返回Strings 或ByteStrings,具体取决于上下文。
下面的代码应该澄清上面的文字。
data Redis a = ...
instance MonadIO Redis where ...
instance Monad Redis where ...
sendCommand :: [String] -> Redis String
class RedisValue a where
decodeValue :: String -> a
-- example instances
instance RedisValue String where ...
instance RedisValue ByteString where ...
不同的上下文,不同的类型
Redis 支持一种简单的事务形式。在事务中,大多数命令可以像在事务之外一样发送。然而,它们的执行会延迟到用户发送提交命令(在 Redis 中称为 exec)。在事务内部,数据存储仅返回一个确认命令已存储以供以后执行。提交后 (exec) 将返回所有存储命令的所有结果。
这意味着上面的get-函数在事务上下文中看起来有点不同:
get :: (RedisStatus a) => Key -> RedisTransaction a
get k = decodeStatus <$> sendCommand ["GET", key]
注意:
一元类型现在是
RedisTransaction以指示事务上下文。a返回类型现在是RedisStatus的任何实例。RedisValue和RedisStatus的实例之间存在重叠。例如String在两个类中。专用的Status数据类型可能仅在RedisStatus类中。
实际问题
我的问题是,我如何编写一个函数get,它可以在两种上下文中工作,并具有适合上下文的返回类型类。我需要的是
一种为
get提供返回类型“Redis 或 RedisTransaction”的方法,类型
a在Redis上下文中是RedisValue的实例,在RedisStatus上下文中是RedisStatus的实例。一个函数
decode根据上下文自动做正确的事情。我认为这必须来自(多参数)类型类。
如果您知道我如何做到这一点,或者有一些示例代码甚至文章的指针,我将不胜感激!
【问题讨论】:
-
您能否创建两个不同的函数 - 一个用于
Redis上下文,一个用于RedisTransaction上下文? -
是否单个
RedisValue可以表示为String或ByteString,或者给定RedisValue的RedisValue是两者之一,但不是两者兼而有之?如果前者为真,@sclv 的解决方案应该可以工作,但后者需要一些额外的技巧。 -
另外,这是一个非常非常好的第一个 SO 问题。请留下来!
-
谢谢,acfoltzer。关于您的问题:单个 RedisValue 可以表示为 String 或 ByteString (甚至是其他东西)。
-
Cable729,是的,我可以。从可用性和新手友好的角度来看,这可能是最好的,因为两个独立的函数(及其类型)
get和getTx比组合函数更容易理解。但是,Redis 有 100 多个命令,其中大部分可以在两种上下文中使用。这会导致大量重复。