представлять состояния вещей на функциональных языках - PullRequest
8 голосов
/ 19 июня 2011

Я играл с функциональными языками (в частности, с 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";
}

Ответы [ 3 ]

8 голосов
/ 19 июня 2011

Ваш пример содержит несколько вещей, которые могли бы работать по-другому на функциональном языке:

  • Класс изменчив - в функциональных языках вы бы использовали неизменный тип
  • Вы используете current_time для получения текущего времени, но это не чистая функция (это зависит от некоторого глобального изменяющегося состояния). В чистых функциональных языках (Haskell) это недопустимо (и вы должны использовать монады), но большинство нечистых функциональных языков (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"

Тип State является неизменным в том смысле, что он никогда не изменяет значение своего локального поля. Это не чисто функционально, потому что это зависит от (изменяющегося) текущего времени, но это не проблема в F # (вы просто должны знать об этом). Если вам нужен какой-то метод, который изменяет состояние (чего у вас нет), то метод вернет новый экземпляр State (точно так же, как .NET string).

Функция main написана с использованием рекурсии вместо цикла - в этом случае это не имеет значения (цикл был бы также хорош в F #). Смысл использования рекурсии в том, что вы можете передать текущее состояние в качестве аргумента и использовать новый экземпляр при выполнении рекурсивного вызова (который существенно меняет текущее состояние во время вычисления).

8 голосов
/ 19 июня 2011

Я немного растерялся из-за того, что вы предполагаете представлять вещи с состоянием на функциональных языках.

В конечном счете, компьютеры, которые мы обычно используем, являются вещами с состоянием.Языки программирования должны справиться с этим фактом на определенном уровне или отказаться от некоторых возможностей своего главного компьютера.

Языки FP справляются с этим фактом либо:

  • Позволяя вамнаписать функции, которые принимают состояние и создают новое состояние (следовательно, функция остается без состояния)
  • Оборачивая концепцию с состоянием в Monad (или в F #, Выражение вычисления )

Что касается вашего кода, вы хотели бы взглянуть на первый из этих вариантов.Перепишите ваши функции, чтобы они принимали текущее время в качестве аргумента.

2 голосов
/ 19 июня 2011

Я мало что знаю о F #, но насколько я понимаю, это очень близко к OCaml. Итак, вот немного OCaml:

Типы записей очень хороши в этом. Вот некоторый эквивалентный код в OCaml:

#load "unix.cma" ;;

type state = { start : float } ;;

let mystate = { start = Unix.time () } in
    let rec check () =
        if Unix.time () -. mystate.start > 30. then
            print_endline "finished"
        else
            check ()
    in
        check () ;;

(Unix time по какой-то причине является плавающей точкой в ​​OCaml; вы можете преобразовать его в int32, если вам от этого станет лучше.)

...