我正在编写一个currying实验,以了解haskell中的多个语句是如何链接在一起的。
这是我到目前为止所得到的
testCurry :: IO ()
testCurry =
(\b ->
(\_ -> putStrLn b)
((\a ->
putStrLn a
) (show 2))
) (show 3)
testCurryExpected :: IO ()
testCurryExpected = do {
a <- return (show 2);
putStrLn a;
b <- return (show 3);
putStrLn b;
}
main :: IO ()
main =
putStrLn "expected: " >>
testCurryExpected >>
putStrLn "given: " >>
testCurry
我知道如果我这样做会有效:
testCurry :: IO ()
testCurry =
(\b ->
(\next -> next >> putStrLn b)
((\a ->
putStrLn a
) (show 2))
) (show 3)
testCurryExpected :: IO ()
testCurryExpected = do {
a <- return (show 2);
putStrLn a;
b <- return (show 3);
putStrLn b;
}
main :: IO ()
main =
putStrLn "expected: " >>
testCurryExpected >>
putStrLn "given: " >>
testCurry
但我不知道如何仅使用函数来模拟&#34;&gt;&gt;&#34;(然后)行为。
我知道a >> b
是根据a >>= \_ -> b
定义的,但我不确定>>=
是如何根据IO a >>= IO b
定义的,但不知道如何将其转化为原始功能组合。
有人可以帮助我让这个实验起作用吗?
简而言之,我想知道在没有&gt;&gt;的情况下是否有办法做到这一点或&gt;&gt; =运算符,也不包装这些运算符。
(\a -> \b -> a >> b)(putStrLn "one")(putStrLn "two")
注意:为了概念起见,我限制自己使用最多一个参数的匿名函数。
编辑:我找到了一个足够好的解决方案,创建了我自己的putStrLn的Free表示,名为Free_PutStrLn,没有解释;使用Lists构建操作链,然后自己进行评估。
data Free_PutStrLn = Free_PutStrLn String deriving Show
eval :: [Free_PutStrLn] -> IO ()
eval a =
foldl (\a -> \b ->
let deconstruct (Free_PutStrLn str) = str in
a >> putStrLn (deconstruct b)
) (return ()) a
testCurry :: [Free_PutStrLn]
testCurry =
(\a ->
[Free_PutStrLn a] ++
((\b ->
[Free_PutStrLn b]
) (show 3))
)(show 2)
main =
putStrLn (show testCurry) >>
eval (testCurry)
JavaScript概念证明:
// | suspends an object within a function context.
// | first argument is the object to suspend.
// | second argument is the function object into which to feed the suspended
// | argument(first).
// | third argument is where to feed the result of the first argument feeded into
// | second argument. use a => a as identity.
const pure = obj => fn => f => f(fn(obj));
// | the experiment
pure({'console': {
'log': str => new function log() {
this.str = str;
}
}})(free =>
pure(str => () => console.log(str))
(putStrLn =>
pure("hello")(a =>
[free.console.log(a)].concat (
pure("world")(b =>
[free.console.log(b)]
)(a => a))
)((result =>
pure(logObj => logObj.str)
(deconstruct =>
result.map(str => putStrLn(deconstruct(str)))
)(result =>
result.forEach(f => f())
)
)
)
)(a => a)
)(a => a)
答案 0 :(得分:3)
但我不知道如何仅使用函数来模拟
>>
(然后)行为。
嗯,你不能! >>
(在这种情况下)是关于排序副作用的。 Haskell函数永远不会产生副作用†。副作用只能在monadic动作中发生,因此可以通过包括>>
在内的monadic组合器进行排序,但是没有Monad
‡约束“这样做以及那个“在Haskell中根本没有任何意义。 Haskell函数不是执行,它只是一个数学变换,其结果可能评估。该结果本身可能是一个实际的动作,例如类型为IO ()
,此类操作可以与其他操作一起执行和/或单一链接。但这实际上与对产生这一行动的函数的评估有些正交。
这就是答案:“我怎样才能让这个currying实验按预期运行?”你不能,你需要使用其中一个monadic组合器(或do
符号,这只是语法的糖)。
还要从一个不同的角度处理这个问题:你不“需要monads”来表达副作用的排序。例如,我可以通过生成例如“定义副作用”来定义一种类型。 Python代码在执行时具有以下效果:
newtype Effect = Effect { pythons :: [String] }
在这里,您可以通过简单地连接指令列表来对效果进行排序。尽管如此,这种排序不是通过任何类型的曲线练习来实现的,而是通过无聊的列表连接来实现的。对此的首选接口是monoid class:
import Data.Monoid
instance Monoid Effect where
mempty = Effect []
mappend (Effect e₀) (Effect e₁) = Effect $ e₀ ++ e₁
然后你可以这样做:
hello :: Effect
hello = Effect ["print('hello')"] <> Effect ["print('world')"]
(<>
只是mappend
的简写同义词。您也可以定义一个自定义运算符,比如#
来链接这些操作,但是如果有标准的话支持某些操作的类通常使用它是个好主意!)
好的,完美的测序,不需要monadic操作员。
但很明显,只是评估 hello
不会导致任何打印:它只会给你一些其他的源代码。您实际上需要将这些指令提供给Python解释器以实现副作用。
原则上与IO
类型没有区别:评估{{1动作也不会导致任何副作用,只将它链接到main(或GHCi repl)。你在子表达式中包含多少个lambdas与此完全无关,因为副作用的出现 nothing 与函数是否在任何地方被调用有关!它只与动作如何链接到实际的“执行者”有关,无论是Python解释器还是Haskell自己的IO
。
如果你现在想知道为什么它必须是那些笨拙的monad如果更简单的main
也能解决问题...... Monoid
的问题在于它没有返回值这样的东西。您可以通过这种方式完美地生成“纯输出”操作,只需执行预定的Python程序,但您永远不能从Python中取回任何值,以便在Haskell中使用以决定接下来应该发生什么。这是monads允许你做的事情。
<小时/> † 是的,从不。 Haskell 不包含名为
unsafePerformIO
的内容。任何在评论中另有声明的人都应suffer nuclear retaliation。