如何让这个currying实验按预期运行?

时间:2017-01-02 23:20:08

标签: haskell currying

我正在编写一个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)

1 个答案:

答案 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

准确地说,the weaker Applicative is sufficient