如何在IO代码中与纯算法交互

Chr*_*ris 2 io monads haskell purely-functional

为了用一个简单的例子说明这一点,请说我已经实现了filter:

filter :: (a -> Bool) -> [a] -> [a]
Run Code Online (Sandbox Code Playgroud)

我有一个p与现实世界相互作用的谓词:

p :: a -> IO Bool
Run Code Online (Sandbox Code Playgroud)

如何在filter不编写单独的实现的情况下使其工作:

filterIO :: (a -> IO Bool) -> [a] -> IO [a]
Run Code Online (Sandbox Code Playgroud)

大概如果我可以p变成p':

p': IO (a -> Bool)
Run Code Online (Sandbox Code Playgroud)

然后我就能做到

main :: IO ()
main = do 
  p'' <- p'
  print $ filter p'' [1..100]
Run Code Online (Sandbox Code Playgroud)

但我一直无法找到转换.

编辑: 正如人们在评论中指出的那样,这样的转换没有意义,因为它会破坏IO Monad的封装.

现在的问题是,我可以构建我的代码,以便纯和IO版本不完全复制核心逻辑吗?

Ale*_*lec 5

如何在不编写单独的实现的情况下使其与过滤器一起使用

这是不可能的,事实上这种事情是不可能的 - 设计--Haskell对其类型设置了严格的限制,你必须遵守它们.你不能IO无所畏惧地洒遍这个地方.

现在的问题是,我可以构建我的代码,以便纯和IO版本不完全复制核心逻辑吗?

你会感兴趣的filterM.然后,您可以filterIO使用IOmonad和使用monad的纯功能来获得两种功能Identity.当然,对于纯粹的情况,您现在必须支付包装/解包(或包装coerce)Identity包装的额外费用.(边注:由于Identitynewtype,这仅仅是一个代码的可读性成本,而不是一个运行一个)

 ghci> data Color = Red | Green | Blue deriving (Read, Show, Eq)
Run Code Online (Sandbox Code Playgroud)

下面是一个monadic示例(注意,只含有该线Red,BlueBlue在提示被用户输入的):

 ghci> filterM (\x -> do y<-readLn; pure (x==y)) [Red,Green,Blue]
 Red
 Blue
 Blue
 [Red,Blue] :: IO [Color]
Run Code Online (Sandbox Code Playgroud)

这是一个纯粹的例子:

 ghci> filterM (\x -> Identity (x /= Green)) [Red,Green,Blue]
 Identity [Red,Blue] :: Identity [Color]
Run Code Online (Sandbox Code Playgroud)