gpt4 book ai didi

haskell - 简化 ReaderT 环境中存储的函数的调用

转载 作者:行者123 更新时间:2023-12-02 19:32:02 25 4
gpt4 key购买 nike

假设我有这样的环境记录:

import Control.Monad.IO.Class
import Control.Monad.Trans.Reader

type RIO env a = ReaderT env IO a

data Env = Env
{ foo :: Int -> String -> RIO Env (),
bar :: Int -> RIO Env Int
}

env :: Env
env =
Env
{ foo = \_ _ -> do
liftIO $ putStrLn "foo",
bar = \_ -> do
liftIO $ putStrLn "bar"
return 5
}

存储在环境中的函数可能具有不同数量的参数,但它们始终会在 RIO Env monad 中生成值,即在 ReaderTIO 由环境本身参数化。

我希望有一种在 RIO Env monad 内调用这些函数的简洁方法。

我可以写这样的call函数:

import Control.Monad.Reader 

call :: MonadReader env m => (env -> f) -> (f -> m r) -> m r
call getter execute = do
f <- asks getter
execute f

并像这样使用它(可能与 -XBlockArguments 结合使用):

 example1 :: RIO Env ()
example1 = call foo $ \f -> f 0 "fooarg"

但是,理想情况下,我希望有一个 call 版本,它允许使用以下更直接的语法,并且仍然适用于具有不同数量参数的函数:

 example2 :: RIO Env ()
example2 = call foo 0 "fooarg"

example3 :: RIO Env Int
example3 = call bar 3

这可能吗?

最佳答案

从这两个示例中,我们可以猜测 call 的类型为 (Env -> r) -> r

example2 :: RIO Env ()
example2 = call foo 0 "fooarg"

example3 :: RIO Env Int
example3 = call bar 3

将其放入类型类中,并考虑两种情况,r 是箭头 a -> r',或者 r 是箭头RIO 环境'。使用类型类实现可变参数通常不受欢迎,因为它们非常脆弱,但它在这里工作得很好,因为 RIO 类型提供了一个自然的基本情况,并且一切都由访问器的类型决定(因此类型推断不会妨碍)。

class Call r where
call :: (Env -> r) -> r

instance Call r => Call (a -> r) where
call f x = call (\env -> f env x)

instance Call (RIO Env r') where
call f = ask >>= f

关于haskell - 简化 ReaderT 环境中存储的函数的调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61642492/

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