我刚刚开始来看看Haskell(我以前的FP体验是在Scheme中),而我came across this code:
do { putStrLn "ABCDE" ; putStrLn "12345" }
对我来说,这是程序式编程,如果有的话 - 特别是因为副作用的连续性。
有人请说明这段代码在任何方面都是“功能性的”吗?
答案 0 :(得分:20)
虽然它似乎是一个程序性程序,但上述语法被翻译成功能程序,如下所示:
do { putStrLn "ABCDE" ; putStrLn "12345" }
=>
IO (\ s -> case (putStrLn "ABCDE" s) of
( new_s, _ ) -> case (putStrLn "12345" new_s) of
( new_new_s, _) -> ((), new_new_s))
也就是说,一系列具有唯一世界参数的嵌套函数穿过它们,对“原始函数”的调用进行“程序性”排序。此设计支持将命令式编程编码为函数式语言。
对此设计所依据的语义决策的最佳介绍是"The Awkward Squad"论文,
答案 1 :(得分:13)
我认为我们不能清楚地回答这个问题,因为“功能性”是一个模糊的概念,并且它的含义存在矛盾的想法。所以我更喜欢彼得兰丁的建议替代术语“外延”,这是精确和实质性的,对我来说,心脏和心灵。函数式编程的灵魂以及它对于等式推理的好处。有关Landin定义的一些提示,请参阅these comments。 IO
不是指示性的。
答案 2 :(得分:5)
以这种方式思考。它实际上并不“执行”IO指令。 IO monad是一个纯值,它封装了要执行的“命令式计算”(但实际上并没有执行它)。您可以使用monad运算符和类似“do”的结构以纯粹的方式将monad(计算)组合成更大的“计算”。尽管如此,本身并没有“执行”。事实上,在某种程度上,Haskell程序的整个目的是将一个大的“计算”组合在一起,即main
值(类型为IO a
)。当你运行程序时,就是运行这个“计算”。
答案 3 :(得分:3)
这是monad。阅读do-notation,了解封面背后的内容。
答案 4 :(得分:2)
请有人说明这段代码
do { putStrLn "ABCDE" ; putStrLn "12345" }
在任何方面都具有“功能性”吗?
这就是我如何看待Haskell I / O的当前情况;通常的免责声明适用于> _ << / p>
如果用“功能性”来表示“完全功能性”,则即刻(2020年6月),这取决于您的Haskell 实现。但这并非总是如此-实际上,Haskell 语言的原始I / O模型真的完全是功能!
在Philip Wadler的How to Declare an Imperative的帮助下,可以回到哈斯克尔的早期旅行了:
import Prelude hiding (IO)
import qualified Prelude (IO)
import Control.Concurrent.Chan(newChan, getChanContents, writeChan)
import Control.Monad((<=<))
-- pared-back emulation of retro-Haskell I/O
--
runDialogue :: Dialogue -> Prelude.IO ()
runDialogue d =
do ch <- newChan
l <- getChanContents ch
mapM_ (writeChan ch <=< respond) (d l)
respond :: Request -> Prelude.IO Response
respond Getq = fmap Getp getChar
respond (Putq c) = putChar c >> return Putp
main = runDialogue (retro_main :: Dialogue)
{-
implementation side
-----------------------------------
========== retro-Haskell ==========
-----------------------------------
language side
-}
-- pared-back definitions for retro-Haskell I/O
-- from page 14 of Wadler's paper
--
data Request = Getq | Putq Char
data Response = Getp Char | Putp
type Dialogue = [Response] -> [Request]
(将其扩展到retro-Haskell的所有I / O上都是非常热衷的读者的一种练习;-)
您去了:纯功能性I / O!响应将流传输到 main
retro_main
,然后将请求流传输回:
拥有如此优雅的纯度,您可以高兴地定义:
-- from page 15 of Wadler's paper
echoD :: Dialogue
echoD p =
Getq :
case p of
Getp c : p' ->
if (c == '\n') then
[]
else
Putq c :
case p' of
Putp : p'' -> echoD p''
您看起来很困惑-没关系;你会明白的:-D
这是A History of Haskell第24页的更复杂的示例:
{- main ~(Success : ~((Str userInput) : ~(Success : ~(r4 : _)))) = [ AppendChan stdout "enter filename\n", ReadChan stdin, AppendChan stdout name, ReadFile name, AppendChan stdout (case r4 of Str contents -> contents Failure ioerr -> "can't open file") ] where (name : _) = lines userInput -}
你还在吗?
是您旁边的垃圾桶吗? ??你病了吗该死的。
那么-也许您会发现,使用易于识别的界面会容易一些:
-- from page 12 of Wadler's paper
--
echo :: IO ()
echo = getc >>= \ c ->
if (c == '\n') then
done
else
putc c >>
echo
-- from pages 3 and 7
--
puts :: String -> IO ()
puts [] = done
puts (c:s) = putc c >> puts s
done :: IO ()
done = return ()
-- based on pages 16-17
--
newtype IO a = MkIO { enact :: Reality -> (Reality, a) }
type Reality = ([Response], [Request])
bindIO :: IO a -> (a -> IO b) -> IO b
bindIO m k = MkIO $ \ (p0, q2) -> let ((p1, q0), x) = enact m (p0, q1)
((p2, q1), y) = enact (k x) (p1, q2)
in
((p2, q0), y)
unitIO :: a -> IO a
unitIO x = MkIO $ \ w -> (w, x)
putc :: Char -> IO ()
putc c = MkIO $ \ (p0, q1) -> let q0 = Putq c : q1
Putp : p1 = p0
in
((p1, q0), ())
getc :: IO Char
getc = MkIO $ \ (p0, q1) -> let q0 = Getq : q1
Getp c : p1 = p0
in
((p1, q0), c)
mainD :: IO a -> Dialogue
mainD main = \ p0 -> let ((p1, q0), x) = enact main (p0, q1)
q1 = []
in
q0
-- making it work
instance Monad IO where
return = unitIO
(>>=) = bindIO
我还提供了您的示例代码;也许会有所帮助:
-- local version of putStrLn
putsl :: String -> IO ()
putsl s = puts s >> putc '\n'
-- bringing it all together
retro_main :: Dialogue
retro_main = mainD $ do { putsl "ABCDE" ; putsl "12345" }
是的:这仍然是纯功能性的I / O;检查retro_main
的类型。
显然,基于对话的I / O最终在空间站中像臭鼬一样受欢迎。将其填充到单子界面中只会将恶臭(及其来源)限制在该站的一小部分内-那时,Haskellers希望这个小臭鼬消失!
因此,Haskell中用于I / O的抽象monadic接口已成为标准-小部分及其辛辣的乘员从空间站分离出来并被拖回地球,那里的新鲜空气更多 丰富。空间站上的气氛有所改善,大多数Haskellers继续做其他事情。
但是有些人对此新的I / O抽象模型感到na恼-was Haskell still purely functional?
如果模型是基于抽象的,那么在这种情况下:
IO
return
(>>=)
,catch
等getArgs
,getEnv
等然后这些实体的实际定义将特定于Haskell的每种实现。
代替询问:
现在应该问的是:
所以您的问题的答案:
请有人说明这段代码
do { putStrLn "ABCDE" ; putStrLn "12345" }
在任何方面都具有“功能性”吗?
现在取决于您使用的Haskell的实现方式。
不是您想要的答案?现在是the skunk ...
答案 5 :(得分:0)
这不是功能代码。为什么会这样?