我正在尝试编写一些 IO 包装的函数。
\n我当前的代码(有效)是:
\ngetUserHome :: IO String\ngetUserHome = do\n usr_id <- getRealUserID\n homeDirectory <$> getUserEntryForID usr_id\nRun Code Online (Sandbox Code Playgroud)\n我正在寻找一种更方便的表示法(不使用 -do关键字的表示法)。
没有任何 Monadic 沙拉我就写
\ngetUserHome = homeDirectory . getUserEntryForID . getRealUserID\nRun Code Online (Sandbox Code Playgroud)\n我猜想还有一个.尊重 Monad 的替代运算符..?但在我所有的寻找中,我还没有找到它。
我尝试过<$>,但这似乎不是我想要的:
src/Main.hs:49:21: error:\n \xe2\x80\xa2 Couldn\'t match type \xe2\x80\x98IO UserEntry\xe2\x80\x99 with \xe2\x80\x98UserEntry\xe2\x80\x99\n Expected type: UserID -> UserEntry\n Actual type: UserID -> IO UserEntry\n \xe2\x80\xa2 In the second argument of \xe2\x80\x98(<$>)\xe2\x80\x99, namely \xe2\x80\x98getUserEntryForID\xe2\x80\x99\n In the first argument of \xe2\x80\x98(<$>)\xe2\x80\x99, namely\n \xe2\x80\x98homeDirectory <$> getUserEntryForID\xe2\x80\x99\n In the expression:\n homeDirectory <$> getUserEntryForID <$> getRealUserID\n |\n49 | homeDirectory <$> getUserEntryForID <$> getRealUserID -- usr_id\nRun Code Online (Sandbox Code Playgroud)\n
你可以使用>>=,因为 Haskell 最终将do块“脱糖”为:
getUserHome :: IO String
getUserHome = getRealUserID >>= \usr_id -> homeDirectory <$> getUserEntryForID usr_id
Run Code Online (Sandbox Code Playgroud)
这可以简化为:
getUserHome :: IO String
getUserHome = getRealUserID >>= fmap homeDirectory . getUserEntryForID
Run Code Online (Sandbox Code Playgroud)
或者:
getUserHome :: IO String
getUserHome = getRealUserID >>= (homeDirectory <$>) . getUserEntryForID
Run Code Online (Sandbox Code Playgroud)
首先始终考虑类型:
getRealUserID :: IO UserId
getUserEntryForID :: UserId -> IO UserEntry
homeDirectory :: UserEntry -> String
Run Code Online (Sandbox Code Playgroud)
现在你可以:
适用getRealUserID于getUserEntryForID. 这直接适合
(>>=) :: m a -> (a -> m b ) -> m b
(>>=) :: IO UserId -> (UserId -> IO UserEntry) -> IO UserEntry
Run Code Online (Sandbox Code Playgroud)
实际上,如果你希望它看起来像一个组合链,我更喜欢翻转版本,即
getUserEntryForID =<< getRealUserID :: IO UserEntry
Run Code Online (Sandbox Code Playgroud)
适用homeDirectory于此。在你的情况下,这根本不是一元的,所以你需要用fmapor来提升它<$>。考虑运算符优先级。
fmap homeDirectory $ getUserEntryForID =<< getRealUserID
homeDirectory <$> (getUserEntryForID =<< getRealUserID)
Run Code Online (Sandbox Code Playgroud)
因为 monad 是关联的,所以你也可以反过来做:
撰写homeDirectory与getUserEntryForID. 后者已经是标准的Kleisli 箭头,您homeDirectory也可以提升到 Kleisli 以便使用 Kleisli 组合运算符:
pure . homeDirectory :: UserEntry -> IO String
pure . homeDirectory <=< getUserEntryForID :: UserId -> IO String
Run Code Online (Sandbox Code Playgroud)
再次将整个事情应用到getRealUserID:
(pure . homeDirectory <=< getUserEntryForID) =<< getRealUserID
Run Code Online (Sandbox Code Playgroud)
事实上,你也可以getRealUserID通过虚拟参数变成克莱斯利箭头()。这种风格在 Haskell 中相当罕见,但它的优点是 Kleisli 组合的结合性变得明显,就像普通函数组合的结合性一样:
pure . homeDirectory <=< getUserEntryForID <=< const getRealUserID $ ()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
179 次 |
| 最近记录: |