gpt4 book ai didi

haskell - 在 hedis 之上构建一个 monad,一个 haskell redis 库

转载 作者:IT王子 更新时间:2023-10-29 06:04:28 24 4
gpt4 key购买 nike

我想在 hedis 之上写一个简单的 DSL , 一个 redis 库。目标是编写如下函数:

iWantThis :: ByteString -> MyRedis ()
iWantThis bs = do
load bs -- :: MyRedis () It fetches a BS from Redis and puts it as
-- the state in a state monad
bs <- get -- :: MyRedis (Maybe ByteString) Gets the current state
put $ doSomethingPure bs -- :: MyMonad () Updates the state
commit -- :: MyRedis () Write to redis

基本思想是从 Redis 中获取数据,将其放入状态 monad 中,对状态进行一些处理,然后将更新后的状态放回 Redis。

显然,它应该是原子的,所以 loadput 应该发生在同一个 Redis 事务中。 Hedis 通过将对 Redis 的调用包装在 RedisTx (Queued a) 中来实现这一点。例如,我们有 get::ByteString -> RedisTx (Queued a)Queued 是一个 monad,然后您在 Queued a 上运行 multiExec 以执行 Queued a 中的所有内容同样的交易。所以我尝试这样定义我的 MyRedis:

import qualified Database.Redis as R
newtype MyRedis a = MyRedis { runMyRedis :: StateT MyState R.RedisTx a } -- deriving MonadState, MyState...

run 函数调用 multiExec 所以我确信只要我留在 MyRedis 中,所有事情都发生在同一个事务中。

run :: MyRedis (R.Queued a) -> MyState -> IO (R.TxResult a)
run m s = R.runRedis (undefined :: R.Connection) (R.multiExec r)
where r = evalStateT (runMyRedis m) s

此外,我可以将 commit 定义为:

commit :: ByteString -> MyRedis (R.Queued R.Status)
commit bs = do
MyState new <- get
(MyRedis . lift) (R.set bs new)

计算看起来像:

computation :: MyRedis (R.Queued R.Status)
computation = do
load gid
MyState bs <- get
put $ MyState (reverse bs)
commit gid
where gid = "123"

但是我想不出“load”怎么写

load :: ByteString -> MyRedis ()
load gid = undefined

其实我觉得load是不行的,因为get的类型是ByteString -> RedisTx (Queued (Maybe ByteString)) 并且我无法在不执行它的情况下查看 Queued monad。

问题:

  1. 由于 Hedis 的 get 类型,定义具有上述语义的 load 函数没有意义是否正确?

  2. 是否可以更改 MyRedis 类型定义以使其工作?

  3. Hedis 没有定义 RedisT monad 转换器。如果存在这样的变压器,它会有什么帮助吗?

  4. Hedis 定义(但不导出给 lib 用户)一个 MonadRedis 类型类;让我的 monad 成为该类型类的实例会有帮助吗?

  5. 这是正确的方法吗?我想:

    • 对 Redis 的抽象(我可能有一天会切换到另一个数据库)
    • 限制我的用户可以使用的Redis函数(基本上只提升到MyRedisgetset)
    • 保证当我运行我的 monad 时,所有事情都发生在同一个 (redis) 事务中
    • 将我的 redis 抽象与我的 monad 中的其他函数放在同一级别

您可以使用 http://pastebin.com/MRqMCr9Q 处的代码.对于 pastebin 很抱歉,lpaste.net 目前已关闭。

最佳答案

你想要的是不可能的。特别是,您不能在一个 Redis 事务中运行计算时提供 monadic 接口(interface)。与您正在使用的库无关 - 这不是 Redis 可以做的事情。

Redis 事务与您在关系数据库世界中可能习惯的 ACID 事务有很大不同。 Redis 事务具有批处理 语义,这意味着后面的命令不能以任何方式依赖于前面命令的结果。

看:这是与您的示例类似的东西,在 Redis 命令行上运行。

> set "foo" "bar"
OK
> multi
OK
> get "foo"
QUEUED -- I can't now set "baz" to the result of this command because there is no result!
> exec
1) "bar" -- I only get the result after running the whole tran

无论如何,这就是该库的有点奇怪的 Queued 类型的目的:这个想法是防止您在批处理结束之前访问批处理命令的任何结果。 (似乎作者想要抽象批处理和非批处理命令,但有更简单的方法可以做到这一点。请参阅下文了解我如何简化事务接口(interface)。)

所以当涉及 Redis 事务时,没有“选择下一步做什么”,但是 (>>=)::m a -> (a -> m b) -> m b 的整个要点是后来的影响可能取决于早期的结果。您必须在 monad 和事务之间做出选择。


如果您决定需要事务,可以使用 Monad 的替代方法,称为 Applicative,它可以很好地支持纯静态效果。这正是我们所需要的。这里有一些(完全未经测试的)代码说明了我将如何为您的想法制作一个应用版本。

newtype RedisBatch a = RedisBatch (R.RedisTx (R.Queued a))
-- being a transactional batch of commands to send to redis

instance Functor RedisBatch where
fmap = liftA

instance Applicative RedisBatch where
pure x = RedisBatch (pure (pure x))
(RedisBatch rf) <*> (RedisBatch rx) = RedisBatch $ (<*>) <$> rf <*> rx

-- no monad instance

get :: ByteString -> RedisBatch (Maybe ByteString)
get key = RedisBatch $ get key

set :: ByteString -> ByteString -> RedisBatch (R.Status)
set key val = RedisBatch $ set key val

runBatch :: R.Connection -> RedisBatch a -> IO (R.TxResult a)
runBatch conn (RedisBatch x) = R.runRedis conn (R.multiExec x)

如果我想抽象出事务性或非事务性行为,就像库作者试图做的那样,我会编写第二种类型 RedisCmd 来公开一个 monadic接口(interface),以及一个包含我的原始操作的类,以及我的两个 RedisBatchRedisCmd 类型的实例。

class Redis f where
get :: ByteString -> f (Maybe ByteString)
set :: ByteString -> ByteString -> f (R.Status)

现在,具有 (Applicative f, Redis f) => ... 类型的计算可以用于任何一种行为(事务性或非事务性),但那些需要 monad ( Monad m, Redis m) => ... 只能在非事务模式下运行。


总而言之,我认为这不值得。人们似乎喜欢像这样在库上构建抽象,总是提供比库少的功能,并为潜伏的错误编写更多代码。每当有人说“我可能想切换数据库”时,我叹息:唯一足够抽象的抽象purpose 是一个不提供任何功能的。担心在需要时切换数据库(也就是说,永远不要)。

另一方面,如果您的目标不是抽象数据库而只是清理界面,最好的办法可能是 fork 库。

关于haskell - 在 hedis 之上构建一个 monad,一个 haskell redis 库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35610524/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com