Есть ли у SML (Poly) CL-подобный REPL?

Вот цитата из Рона Гаррета "Лиспинг в JPL":

"Отладка программы, работающей на оборудовании стоимостью 100 миллионов долларов, находящемся на расстоянии 100 миллионов миль, представляет собой интересный опыт. Наличие цикла чтения-проверки-печати на космическом корабле доказало свою неоценимость при поиске и устранении проблемы".

Как новичок, пытающийся решить, куда мне ввязываться, я склоняюсь к ML, потому что бывший профессор бредил, и я нахожу много книг, которые объединяют обсуждения Lambda Calculus с ML и ML, выглядит довольно вменяемым. (Я в конечном итоге собираюсь научить этому.)

Итак, есть ли у ML REPL, где, подобно Лиспу, вы можете просто "добавить больше кода" во время его работы, то есть, может ли аппарат господина Гаррета за 100 миллионов долларов работать на ML?

2 ответа

По умолчанию Poly/ML запускается с REPL, что аналогично SML/NJ. Более того, вы можете легко вызывать компилятор во время выполнения: он создаст для вас собственный код и добавит его в среду ML в порядке eval в LISP, но это статически типизированный и реальный машинный код, а не интерпретируемый.

Структура PolyML.Compiler предоставляет различные (относительно стабильные) интерфейсы для этого, помимо официального стандарта SML.

Вот рабочий пример для Poly/ML 5.5 или 5.6:

fun eval text =
  let
    fun print s = (TextIO.output (TextIO.stdOut, s); TextIO.flushOut TextIO.stdOut);

    val line = ref 1;
    val in_buffer = ref (String.explode text);
    val out_buffer = ref ([]: string list);

    fun output () = String.concat (rev (! out_buffer));

    fun get () =
      (case ! in_buffer of
        [] => NONE
      | c :: cs => (in_buffer := cs; if c = #"\n" then line := ! line + 1 else (); SOME c));
    fun put s = out_buffer := s :: ! out_buffer;
    fun put_message {message = msg1, hard, location = {startLine = line, ...}, context} =
     (put (if hard then "Error: " else "Warning: ");
      PolyML.prettyPrint (put, 76) msg1;
      (case context of NONE => () | SOME msg2 => PolyML.prettyPrint (put, 76) msg2);
      put ("Line " ^ Int.toString line ^ "\n"));

    val parameters =
     [PolyML.Compiler.CPOutStream put,
      PolyML.Compiler.CPErrorMessageProc put_message,
      PolyML.Compiler.CPLineNo (fn () => ! line)];
    val _ =
      (while not (List.null (! in_buffer)) do
        PolyML.compiler (get, parameters) ())
      handle exn =>
        (put ("Exception- " ^ General.exnMessage exn ^ " raised");
          print (output ()); raise exn);
  in print (output ()) end;

Теперь мы можем вызвать это в обычном Poly/ML REPL следующим образом:

> eval "1 + 1";
val it = 2: int
val it = (): unit
> eval "fun f 0 = 1 | f n = n * f (n - 1)";
val f = fn: int -> int
val it = (): unit
> eval "f 42";
val it = 1405006117752879898543142606244511569936384000000000: int
val it = (): unit
> f 42;
val it = 1405006117752879898543142606244511569936384000000000: int

Это дает вам метапрограммирование в стиле LISP в статически типизированном мире SML.

Обратите внимание, что структура PolyML.Compiler имеет дополнительные параметры для управления поведением вызова компилятора во время выполнения. Об этом лучше спросить в списке рассылки polyml.

У PolyML есть REPL. Я не знаю его деталей, но если он похож на SML/NJ, вы не сможете использовать его для запуска работающей программы. Если вы хотите сделать это, Common Lisp или Squeak - ваши лучшие ставки - большинство остального сообщества языков программирования считают идею обновления живой программы, поскольку она работает как плохая (или, по крайней мере, слишком опасная для -имеет доступную по умолчанию) идею.

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

Другие вопросы по тегам