Haskell:Monad变形金刚和全球状态

时间:2017-07-18 09:51:58

标签: haskell monad-transformers state-monad

我正在努力学习Haskell。我正在尝试编写一个包含“全局状态”的程序:{# file1.twig #} {% extends "main.twig" %} {% block content %}content text{% endblock %} {# main.twig #} {% block content %}{% endblock %} {% for widget in widgets %} {% embed widget %}{% endembed %} {% endfor %} {% block js %}{% endblock %} {# widget1.twig #} {% block js %} script1 {% endblock %} {# widget2.twig #} {% block js %} script1 {% endblock %} 。我想在每次调用函数时更改状态的组件(例如content text script1 script2 )。改变可以是组件上的简单功能(例如+4)。此外,它打印出更改的组件。这是我到目前为止所做的事情(但我被困住了)。编辑:运行代码后,我想看到最新版本的全局状态。

boxplot(train$rate)

1 个答案:

答案 0 :(得分:2)

让我们尝试从更简单的小部件开始逐步解决您的问题。它在编程和FP方面的重要技能以很好的方式教你技能。此外,使用State monad,尤其是monad-transformer中的多种效果,可以帮助您推理效果并更好地理解事物。

  1. 您希望更新不可变数据类型中的var1。这只能通过创建新对象来完成。所以让我们写下这样的功能:

    plusFour :: Vars -> Vars
    plusFour (Vars v1 v2) = Vars (v1 + 4) v2
    

    Haskell中有很多方法可以将这个功能写得更短,虽然不太容易理解,但我们现在不关心这些功能。

  2. 现在,您希望在State monad中使用此函数来更新不可变状态,并通过此模拟可变性。只有通过查看类型签名才能告诉我这个函数:change :: StateT Vars IO a?我们可以说这个函数有几个效果:它可以访问Vars状态,它可以执行任意IO个动作。此函数还返回类型a的值。嗯,这最后一个很奇怪。什么是a?这个函数应该返回什么?在命令式编程中,此函数将具有类型voidUnit。只是事情,它不会归还所有东西。仅更新上下文。因此,它的结果类型应为()。它可以是不同的。例如,我们可能希望在更改后返回新的Vars。但这通常是编程中不好的方法。它使这个功能更加复杂。

  3. 在我们理解了应该具有什么类型的函数之后(尝试始终从定义类型开始),我们可以实现它。我们想要改变我们的状态。具有与我们的上下文的有状态部分一起操作的功能。基本上,你对这个感兴趣:

    modify :: Monad m => (s -> s) -> StateT s m ()

    modify函数采用更新状态的函数。运行此函数后,您可以观察到根据传递的函数修改了状态。现在change可以这样写:

    change :: StateT Vars IO ()
    change = modify plusFour
    

    您可以仅使用modifychange函数实现put(以及get,这对初学者来说非常好。)

  4. 现在让我们从其他函数调用change函数。在这种情况下,呼叫意味着什么?这意味着您执行monadic action change。此操作会更改您的上下文,您不会关心它的结果,因为它是()。但是如果你在get之后运行change函数(将整个状态绑定到变量),你可以观察到新的变化。如果您只想打印已更改的组件,例如var1,则可以使用gets功能。而且,sample应该具有哪种类型?应该归还什么?如果在来电方面你只对结果状态感兴趣,那么,它应该是(),如下所示:

    sample :: StateT Vars IO ()
    sample = do
        change
        v1 <- gets var1
        liftIO $ print v1
        change
        v1' <- gets var1 
        liftIO $ print v1'  -- this should be v1 + 4
    
  5. 这应该让你对正在发生的事情有所了解。 Monad变形金刚需要一些时间来适应它们,虽然它是一个强大的工具(不完美但非常有用)。

    作为旁注,我想补充一点,使用常见的Haskell设计模式可以更好地编写这些函数。但是你现在不需要关心这些,只是试着了解这里发生了什么。