编写IO操作



我正在尝试编写一些IO封装的函数。

我目前的代码(有效(是:

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

我正在寻找一种更方便的表示法(不使用do-关键字(。

如果没有任何Monadic沙拉,我只会写

getUserHome = homeDirectory . getUserEntryForID . getRealUserID

我想.有一个替代运算符,它尊重Monad。。?但在我所有的搜索中,我都没有找到它


我试过<$>,但这似乎不是我想要的:

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

您可以使用>>=,因为Haskell最终"desugars";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

首先要考虑类型:

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

现在您可以:

  1. 应用getRealUserIDgetUserEntryForID。这直接适合

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

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

    getUserEntryForID =<< getRealUserID :: IO UserEntry
    
  2. 对此应用homeDirectory。在你的情况下,这根本不是一元的,所以你需要用fmap<$>来提升它。考虑运算符优先级。

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

因为单子是关联的,你也可以用另一种方法:

  1. getUserEntryForID组成homeDirectory。后者已经是一个标准的Kleisli箭头,您也可以将homeDirectory提升到Kleisli,以便使用Kleisli合成运算符:

    pure . homeDirectory :: UserEntry -> IO String
    pure . homeDirectory <=< getUserEntryForID :: UserId -> IO String
    
  2. 再次将整件事应用于getRealUserID:

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

事实上,您也可以使用伪()参数将getRealUserID转换为Kleisli箭头。这种风格在Haskell中并不常见,但它的优点是Kleisli组合的结合性变得明显,就像正常函数组合一样:

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

最新更新