用函数式语言表示有状态的东西

jam*_*o00 8 .net f# functional-programming

我一直在玩函数式语言(特别是F#),我真的很喜欢整个不可变/概念.但是,我对你如何用函数式语言表示有状态的东西感到有些迷茫.

例如,如何在函数式语言中重写以下内容?(任何功能语言都很好......只需要绕过它)

class state
{
    int current_time;
    bool is_completed() { 
        return current_time() - start_time > 30 seconds
    }
    double get_progress() { 
        return (current_time() - start_time) / 30 seconds
    }
    void start() {
        start_time = current_time();
    }
}
void main() {
    state s;
    s.start();
    while(s.is_completed() == false) { 
          print s.get_progress();
    }
    print "finished";
}
Run Code Online (Sandbox Code Playgroud)

Mer*_*ham 8

我对你如何用功能语言表示有状态的东西感到有些迷茫.

最终,我们通常使用的计算机是有状态的东西.编程语言必须在某种程度上应对这一事实,或者放弃其主机的某些功能.

FP语言通过以下两种方式处理这一事实:

  • 允许你编写带状态的函数,并产生新的状态(从而使函数成为无状态)
  • Monad中包含有状态概念(或在F#中,计算表达式)

至于您的代码,您需要查看第一个选项.重写您的函数以接受当前时间作为参数.


Tom*_*cek 8

您的示例包含一些在函数式语言中有所不同的方法:

  • 该类是可变的 - 在函数式语言中,您将使用不可变类型
  • current_time用来获取当前时间,但这不是一个纯函数(它取决于一些全局变化的状态).在函数式语言(Haskell)中,这是不允许的(并且你必须使用monad),但是大多数不纯的函数语言(F#,OCaml)都允许这样做.
  • 你的main函数使用循环 - 通常不鼓励在函数式语言中使用循环(尽管有些人支持它们).

惯用的F#解决方案将处理第一个和最后一个点,如下所示:

let currentTime() =
  System.DateTime.Now

type State(startTime) =
  static member Start() =
    State(currentTime())
  member x.IsCompleted =
    (currentTime() - startTime).TotalSeconds > 30.0
  member x.Progress =
    (currentTime() - startTime).TotalSeconds / 30.0

let main() =
  let s = State.Start()
  let rec loop () =
    if not s.IsCompleted then
      printf "%A" s.Progress
      loop ()
  loop ()
  printf "finished"
Run Code Online (Sandbox Code Playgroud)

State从某种意义上说,类型是不可变的,它永远不会改变其本地字段的值.它不是纯粹的功能,因为它取决于(改变)当前时间,但这不是F#中的问题(你只需要知道这一点).如果你需要一些修改状态的方法(你不需要),那么该方法将返回一个新的实例State(就像.NET一样string).

main函数是使用递归而不是循环编写的 - 在这种情况下,它并不重要(循环在F#中也可以).使用递归的关键是您可以将当前状态作为参数传递,并在进行递归调用时使用新实例(这实际上会在计算期间更改当前状态).