Мы использовали элементарные арифметические операции, комбинировали их и абстрагировали получившиеся составные операции путем определения составных процедур. Но всего этого еще недостаточно, чтобы сказать, что мы умеем программировать. Положение, в котором мы находимся, похоже на положение человека, выучившего шахматные правила, но ничего не знающего об основных дебютах, тактики и стратегии. Подобно шахматисту – новичку, мы пока ничего не знаем об основных схемах использования понятий в нашей области знаний. Нам недостает знаний о том, какие именно ходы следует делать (какие именно процедуры имеет смысл определять), и не хватает опыта предсказания последствий сделанного хода (выполнения процедуры). Способность предвидеть последствия рассматриваемых действий необходима для того, чтобы стать квалифицированным программистом, — равно как и для любой другой синтетической, творческой деятельности. Например, квалифицированному фотографу нужно при взгляде на сцену понимать, насколько темным каждый ее участок покажется после печати при разном выборе экспозиции и разных условиях обработки. Только после этого, можно проводить обратные рассуждения и выбирать кадр, освещение, экспозицию и условия обработки так, чтобы получить желаемый результат. Чтобы стать специалистами, нам надо научиться представлять процессы, генерируемые различными типами процедур. Только развив в себе такую способность, мы сможем научиться надежно строить программы, которые ведут себя так, как нам надо.
Структура и интерпретация компьютерных программ.
В ряде разделов я использую главу с названием “функциональная композиция” и мне следует объяснить, что я под этим подразумеваю. Композиция – это построение чего-то сложного из чего-то простого. Стоит понимать, что сложность – это контекстуальное определение. В неких рамках что-то сложное может представать более простым, в результате чего мы можем построить нечто еще более сложное.
Композиция позволяет нам строить сложную иерархическую систему, в которой каждый уровень максимально понятен и независим от других. И один из способов построения абстракции – это функция или один из частных случаев – метод. С помощью методов мы можем строить слои и разбивать программу на различные кусочки. Чёткое проведение границ позволяет следовать принципу “разделяй и властвуй”.
Когда мы говорим, что некая функция работает с другими функциями, мы говорим о композиции. Так функции низшего порядка позволяют строить более сложные функции и помогают избавиться им от дублирования логики. С каждым шагом мы будем подниматься по уровням, и на самом верхнем будем оперировать понятиями решаемой задачи. С помощью абстракций от деталей мы описываем язык предметной области, на котором и записываем основную логику программы.
Однородность
Когда мы работаем на одном уровне абстракции, нам не следует опускаться с него слишком низко или подниматься слишком высоко. Когда мы говорим об иерархической системе, на одном уровне иерархии должны быть однородные сущности, и они не должны работать со слишком низким уровнем или слишком высоким.
Плохая идея в сущности банковского аккаунта работать с сокетами, хоть это и решает некоторую задачу. Даже если вы будете работать с абстракцией ISocket – ничего не изменится. Даже если вы работаете с абстракцией, то это не значит, что вы работаете со слоем своего уровня.
Отличие функции от процедуры
В языке C# нет понятия процедуры, но есть понятие функции и метода. Функция – это более общее понятие, которое включает в себя метод. Помимо этого, функцией также является анонимный метод и лямбда выражение.
Чаще всего понятие функции и процедуры является взаимозаменяемым. Но при более строгом обсуждении под функциями обычно подразумевается чистая функция (Pure Function), её особенностью является то, что она детерминирована от своих параметров и не несет побочных эффектов.
Процедуре же позволительно работать, как и с глобальным состоянием, так и возвращать результат, зависящий от него. Помимо прочего, она вообще может не иметь результата как такового. Это не означает, что мы не можем создавать композицию процедур. Всё-таки мы освобождены от математического определения этого термина и можем основывать свое поведение не только на возвращаемом значении процедуры, но и поведении, которое она определяет.
Функция логирования вполне может быть основана на процедуре записи в файл, хотя понятно, что ей ничего не нужно, кроме непосредственного выполнения и записи в файл. При этом тот, кто работает с функцией логирования, не особо заинтересован в тонкостях этого процесса.
В конечном итоге, зачастую функции и процедуры для нас являются равноправным способом построения абстракций и различия нас не особо интересуют, гораздо важней то, что их объединяет.