Категории
Самые читаемые
ChitatKnigi.com » 🟢Документальные книги » Биографии и Мемуары » Кодеры за работой. Размышления о ремесле программиста - Питер Сейбел

Кодеры за работой. Размышления о ремесле программиста - Питер Сейбел

Читать онлайн Кодеры за работой. Размышления о ремесле программиста - Питер Сейбел
1 ... 78 79 80 81 82 83 84 85 86 ... 192
Перейти на страницу:

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать

Но вернемся к общим положениям. С ленивым механизмом вычисления сложнее предсказать, когда понадобится вычислить выражение. И если вы хотите вывести что-нибудь на экран, то язык с вызовом по значению, где порядок вычисления явно определен, делает это при помощи “функции” с побочным эффектом - я специально ставлю кавычки, так как это вовсе не функция, - с типом, скажем, string -> unit. При вызове функции она печатает что-то на экране - в виде побочного эффекта. Это есть в Лисп, в ML, в любом языке с вызовом по значению.

А в чистом языке, если есть функция string -> unit, ее никогда не надо вызывать: вы ведь знаете, что она выдаст всего лишь ответ типа unit. Больше она не делает ничего. А каков ответ, вы знаете. Но поскольку функция с побочными эффектами, очень важно вызвать ее. В ленивом языке проблема вот какая: вы говорите “f применяется к print 'привет”, а вычисляет ли f свой первый аргумент, для вас неясно. Это все происходит внутри функции. Если же аргументов будет два — print 'привет' и print пока', - она может выполнить один, или оба в любом порядке, или ни одного. Поэтому при ленивых вычислениях делать ввод /вывод при помощи побочных эффектов невозможно. Вы не можете написать таким способом рациональную, надежную, предсказуемую программу. Сначала это было непривычно - нет, собственно, никакого ввода/вывода. И потому долгое время в программах использовалась только функция string -> string. Целая программа делала только это. Строка на входе была вводом, а строка результата - выводом. Вот и все.

Можно было слегка усложнить схему, закодировав в строке вывода команды вывода, которые выполнялись внешним интерпретатором. Строка вывода могла скомандовать: “Вывести вот это на экран, а вон то сохранить на диске”. Интерпретатор уже умел это делать. Итак, мы имели замечательную, чисто функциональную программу, а нехороший интерпретатор интерпретировал командную строку. Но если считываешь файл, как вернуть ввод в программу? Просто: сделать строку с командами вывода, которые интерпретируются нехорошим интерпретатором, и произвести ленивое вычисление - результат поступает обратно на вход. Итак, теперь программа преобразует поток ответов в поток запросов. Поток запросов направляется на нехороший интерпретатор, каждый запрос генерирует ответ, который идет затем на вход. Поскольку вычисление ленивое, программа выдает ответ с таким расчетом, чтобы он успел пройти цикл и прийти на вход. Правда, это работало не без сбоев - если ответ поглощался слишком агрессивно, программа зависала, ведь нужен был ответ на вопрос, которого еще не было на выходе.

Смысл в том, что ленивость загнала нас в угол, и пришлось решать вопрос с вводом/выводом. Это была самая важная проблема ленивого программирования. Но начиналось-то все с другого - с того, как это прикольно, как здорово.

Сейбел: За все то время, что вы занимаетесь программированием, как изменились ваши представления о нем как таковом?

Пейтон-Джонс: Думаю, больше всего это связано с монадами и системами типизации. В начале 1980-х я думал лишь о чисто функциональном программировании с относительно простой системой типизации. Теперь же я думаю о нем как о сочетании функционального, императивного и параллельного программирования, причем связь между ними идет через монады. Типы стали гораздо сложнее, позволяя создавать намного более широкий спектр программ, чем я предусматривал когда-то. Можно считать это эволюцией моих взглядов на программирование.

Сейбел: После первой неудачной попытки вы написали не один компилятор. Наверняка вы поняли, в чем секрет создания успешного компилятора.

Пейтон-Джонс: Да, я много чего понял с тех пор. То был компилятор для императивного языка на императивном языке. Теперь я пишу компиляторы для функционального языка на функциональном языке. Но в GHC, нашем компиляторе для Haskell, важно то, что в нем применен промежуточный язык с типизацией.

Сейбел: А в этом языке применена типизация исходного языка?

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

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

Сейбел: Получается, что есть программы, которые корректны, потому что были получены корректным преобразованием исходного кода, но в то же время, если бы были написаны от руки, компилятор бы сказал, что не может вывести типы.

Пейтон-Джонс: Правильно. Такова природа систем со статической типизацией - и вот почему динамические языки по-прежнему важны и интересны. Можно написать программы, для которых не установить конкретную систему типизации, но которые не дают сбоев при выполнении. Это и есть золотой стандарт - нет аварийных сбоев, не добавляются целые числа к символам. Это будут отличные программы.

Сейбел: Когда сторонники динамической и статической типизации начинают препираться, первые говорят: “Очень много программ, где статическая типизация мешает мне написать то, что я хочу”. А вторые отвечают: “Да, такие программы есть, но на практике это не составляет проблемы”. Что вы думаете по этому поводу?

Пейтон-Джонс: Это отчасти зависит от привычки. Я, например, говорю, что так и не привык писать на C++. С другой стороны, вы не будете страдать от отсутствия ленивых вычислений, если вообще ими не пользовались, а я буду, потому что пользуюсь ими постоянно. Возможно, динамическая типизация - похожий случай. Мое ощущение - насколько оно может быть ценным с моим специфическим опытом - таково, что крупные программные блоки вполне могут иметь статическую типизацию, особенно в таких богатых системах типизации. И там, где такая типизация возможна, она очень полезна по неоднократно приводившимся причинам.

Реже приводится такой довод, как поддержка программ. Если вы хотите поменять кусок своего кода трехлетней давности - не подретушировать одну процедуру, а переписать коренным образом, - то системы типизации будут крайне полезны.

(adsbygoogle = window.adsbygoogle || []).push({});
1 ... 78 79 80 81 82 83 84 85 86 ... 192
Перейти на страницу:
Открыть боковую панель
Комментарии
Jonna
Jonna 02.01.2025 - 01:03
Страстно🔥 очень страстно
Ксения
Ксения 20.12.2024 - 00:16
Через чур правильный герой. Поэтому и остался один
Настя
Настя 08.12.2024 - 03:18
Прочла с удовольствием. Необычный сюжет с замечательной концовкой
Марина
Марина 08.12.2024 - 02:13
Не могу понять, где продолжение... Очень интересная история, хочется прочесть далее
Мприна
Мприна 08.12.2024 - 01:05
Эх, а где же продолжение?