Читаем Кодеры за работой. Размышления о ремесле программиста полностью

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

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

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

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

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

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

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

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

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

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

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

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

Перейти на страницу:

Похожие книги

Адмирал Советского Союза
Адмирал Советского Союза

Николай Герасимович Кузнецов – адмирал Флота Советского Союза, один из тех, кому мы обязаны победой в Великой Отечественной войне. В 1939 г., по личному указанию Сталина, 34-летний Кузнецов был назначен народным комиссаром ВМФ СССР. Во время войны он входил в Ставку Верховного Главнокомандования, оперативно и энергично руководил флотом. За свои выдающиеся заслуги Н.Г. Кузнецов получил высшее воинское звание на флоте и стал Героем Советского Союза.В своей книге Н.Г. Кузнецов рассказывает о своем боевом пути начиная от Гражданской войны в Испании до окончательного разгрома гитлеровской Германии и поражения милитаристской Японии. Оборона Ханко, Либавы, Таллина, Одессы, Севастополя, Москвы, Ленинграда, Сталинграда, крупнейшие операции флотов на Севере, Балтике и Черном море – все это есть в книге легендарного советского адмирала. Кроме того, он вспоминает о своих встречах с высшими государственными, партийными и военными руководителями СССР, рассказывает о методах и стиле работы И.В. Сталина, Г.К. Жукова и многих других известных деятелей своего времени.Воспоминания впервые выходят в полном виде, ранее они никогда не издавались под одной обложкой.

Николай Герасимович Кузнецов

Биографии и Мемуары
100 великих гениев
100 великих гениев

Существует много определений гениальности. Например, Ньютон полагал, что гениальность – это терпение мысли, сосредоточенной в известном направлении. Гёте считал, что отличительная черта гениальности – умение духа распознать, что ему на пользу. Кант говорил, что гениальность – это талант изобретения того, чему нельзя научиться. То есть гению дано открыть нечто неведомое. Автор книги Р.К. Баландин попытался дать свое определение гениальности и составить свой рассказ о наиболее прославленных гениях человечества.Принцип классификации в книге простой – персоналии располагаются по роду занятий (особо выделены универсальные гении). Автор рассматривает достижения великих созидателей, прежде всего, в сфере религии, философии, искусства, литературы и науки, то есть в тех областях духа, где наиболее полно проявились их творческие способности. Раздел «Неведомый гений» призван показать, как много замечательных творцов остаются безымянными и как мало нам известно о них.

Рудольф Константинович Баландин

Биографии и Мемуары
100 великих интриг
100 великих интриг

Нередко политические интриги становятся главными двигателями истории. Заговоры, покушения, провокации, аресты, казни, бунты и военные перевороты – все эти события могут составлять только часть одной, хитро спланированной, интриги, начинавшейся с короткой записки, вовремя произнесенной фразы или многозначительного молчания во время важной беседы царствующих особ и закончившейся грандиозным сломом целой эпохи.Суд над Сократом, заговор Катилины, Цезарь и Клеопатра, интриги Мессалины, мрачная слава Старца Горы, заговор Пацци, Варфоломеевская ночь, убийство Валленштейна, таинственная смерть Людвига Баварского, загадки Нюрнбергского процесса… Об этом и многом другом рассказывает очередная книга серии.

Виктор Николаевич Еремин

Биографии и Мемуары / История / Энциклопедии / Образование и наука / Словари и Энциклопедии