haskell 编写IO操作

jxct1oxe  于 2022-11-14  发布在  其他
关注(0)|答案(2)|浏览(199)

我正在尝试编写一些IO Package 函数。
我当前的代码(有效)是:

getUserHome :: IO String
getUserHome = do
    usr_id <- getRealUserID
    homeDirectory <$> getUserEntryForID usr_id

我正在寻找的是一个更方便的符号(一个没有使用do-关键字)。
如果没有任何的Monadic沙拉我只会写

getUserHome = homeDirectory . getUserEntryForID . getRealUserID

我猜.有一个替代运算符,它尊重单子......但是在我所有的搜索中,我没有找到它。
我尝试了<$>,但这似乎不是我想要的:

src/Main.hs:49:21: error:
    • Couldn't match type ‘IO UserEntry’ with ‘UserEntry’
      Expected type: UserID -> UserEntry
        Actual type: UserID -> IO UserEntry
    • In the second argument of ‘(<$>)’, namely ‘getUserEntryForID’
      In the first argument of ‘(<$>)’, namely
        ‘homeDirectory <$> getUserEntryForID’
      In the expression:
        homeDirectory <$> getUserEntryForID <$> getRealUserID
   |
49 |   homeDirectory <$> getUserEntryForID  <$> getRealUserID -- usr_id
zpjtge22

zpjtge221#

您可以使用>>=,因为Haskell最终会将do块“反糖化”为:

getUserHome :: IO String
getUserHome = getRealUserID >>= \usr_id -> homeDirectory <$> getUserEntryForID usr_id

这可以简化为:

getUserHome :: IO String
getUserHome  = getRealUserID >>= fmap homeDirectory . getUserEntryForID

或:

getUserHome :: IO String
getUserHome  = getRealUserID >>= (homeDirectory <$>) . getUserEntryForID
aiqt4smr

aiqt4smr2#

首先要考虑类型:

getRealUserID     ::              IO UserId
getUserEntryForID :: UserId    -> IO UserEntry
homeDirectory     :: UserEntry ->    String

现在您可以:
1.将getRealUserID应用于getUserEntryForID。这是一个直接适合于

(>>=) :: m  a      -> (a      -> m  b        ) -> m  b
(>>=) :: IO UserId -> (UserId -> IO UserEntry) -> IO UserEntry

事实上,如果你想让它看起来像一个合成链,我更喜欢翻转的版本,即。

getUserEntryForID =<< getRealUserID :: IO UserEntry

1.将homeDirectory应用于此。在您的情况下,这根本不是一元的,所以您需要使用fmap<$>来提升它。请考虑运算符的优先级。

fmap homeDirectory $ getUserEntryForID =<< getRealUserID
homeDirectory <$> (getUserEntryForID =<< getRealUserID)

因为单子是关联的,你也可以反过来做:
1.用getUserEntryForID合成homeDirectory。后者已经是标准的 *Kleisli箭头 *,为了使用Kleisli合成运算符,您也可以将homeDirectory提升到Kleisli:

pure . homeDirectory :: UserEntry -> IO String
pure . homeDirectory <=< getUserEntryForID :: UserId -> IO String

1.同样,将整个过程应用到getRealUserID

(pure . homeDirectory <=< getUserEntryForID) =<< getRealUserID

事实上,你也可以把getRealUserID变成Kleisli箭头,用一个伪参数()。这种风格在Haskell中相当少见,但它的优点是Kleisli组合的结合性变得明显,就像普通函数组合一样明显:

pure . homeDirectory <=< getUserEntryForID <=< const getRealUserID $ ()

相关问题