Есть ли у 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. На мой взгляд, это канонический функциональный язык. Понимание этого позволяет легко понять, почему функциональное программирование является мощным, а также помогает вам понять весь спектр функциональных языков программирования по их отклонениям от него.