如何在 Haskell 中编写 Monad 来高效地跟踪和预取 i18n 键?

问题描述 投票:0回答:1

我正在尝试编写一个 Monad 来跟踪一段代码正在使用哪些 i18n 键。这个想法是预先以有效的方式预取所有这些键的值,而不是在代码执行期间最终出现“N+1”的情况。

这是我到目前为止所拥有的(代码在最后给出)。我该如何为此编写 Monad 实例?有可能吗?还有其他想法可以做到这一点吗?只要 devex/人体工程学是可管理的,我也可以在类型级别跟踪按键。

我思考这个问题的另一种方式是,我需要代码以“两个阶段”运行:

  1. 第一阶段概念上返回两个值:
    [I18nKey]
    - 代码所依赖的 i18n 键列表和
    [(I18nKey, I18nStr)] -> m Text
    一个未应用的函数,需要关联列表中的 i18n 键来生成最终值。
  2. 第二阶段使用单个数据库查询获取转换
    [I18nKey] -> [(I18nKey, I18nStr)]
    并将它们应用于函数以获得最终值。

如何以符合人体工程学的方式用 Haskell 代码表达上述内容?

import UnliftIO.IORef
import Data.Text
import Control.Monad.Reader
import qualified Data.List as DL

type I18nKey = Text
type I18nStr = Text

data I18nEnv m a = I18nEnv
  { envI18nExpectedKeys :: [I18nKey]
  , envI18nVal :: ReaderT [(I18nKey, I18nStr)] m a
  }

instance (Functor m) => Functor (I18nEnv m) where
  {-# INLINE fmap #-}
  fmap f env = 
    env{envI18nVal=fmap f (envI18nVal env)}

instance (Applicative m) => Applicative (I18nEnv m) where
  {-# INLINE pure #-}
  pure x = 
    I18nEnv { envI18nExpectedKeys = [], envI18nVal = pure x }

  {-# INLINE (<*>) #-}
  (<*>) fn a = I18nEnv
    { envI18nExpectedKeys=(envI18nExpectedKeys fn <> envI18nExpectedKeys a)
    , envI18nVal=(envI18nVal fn) <*> (envI18nVal a)
    }
haskell monads reader-monad writer-monad
1个回答
0
投票

这是不可能的。暂时忘记

ReaderT
,您的类型基本上是
([I18nKey], m a)
;为了使它成为一个 monad,你必须实现
join :: ([I18nKey], m ([I18nKey], m a)) → ([I18nKey], m a)
,但是一般来说没有办法从
[I18nKey]
下提取内部
m

将通用单子

m
Writer [I18nKey]
组合的正确方法是使用
WriterT [I18nKey] m
,它会转换为
m ([I18nKey], a)
,但这似乎不适合您的用例。

你确定你需要一个 monad 吗?看看你打算如何使用这个 monad 的例子会有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.