Марсело Кантос дал довольно хорошее объяснение , но я думаю, что это можно сделать чуть точнее.
Тип вещи может быть составлен, когда несколько экземпляров могут быть объединены определенным образом для производства одного и того же типа вещи.
Возможность компоновки структуры управления. В таких языках, как C, проводится различие между выражениями , которые могут быть составлены с использованием операторов для создания новых выражений, и выражениями , которые могут быть составленным с использованием управляющих структур, таких как if
, for
и «структуры управления последовательностью», которая просто выполняет операторы по порядку. Дело в том, что эти две категории не находятся в равных условиях - многие управляющие структуры используют выражения (например, выражение, оцениваемое if
, чтобы выбрать, какую ветвь выполнять), но выражения не могут использовать управляющие структуры. (например, вы не можете вернуть цикл for
). Хотя может показаться сумасшедшим или бессмысленным желание «возвращать цикл for
», на самом деле общая идея трактовать управляющие структуры как первоклассные объекты, которые можно хранить и передавать, не только возможна, но и полезна. В ленивых функциональных языках, таких как Haskell, управляющие структуры, такие как if
и for
, могут быть представлены как обычные функции, которыми можно манипулировать в выражениях, как и любым другим термином, позволяя таким вещам, как функции, которые «строят» другие функции в соответствии с Параметры, которые они передают, и возвращают их вызывающей стороне. Таким образом, хотя C (например) делит «то, что программист может захотеть сделать» на две категории и ограничивает способы объединения объектов из этих категорий, Haskell (например) имеет только одну категорию и не накладывает таких ограничений так что в этом смысле он обеспечивает большую сочетаемость.
Возможность компоновки потоков. Я предполагаю, что, как и Марсело Кантос, вы действительно говорите о возможности компоновки потоков, использующих блокировки / мьютексы. Это немного более сложный случай, потому что на первый взгляд у может есть потоки, которые используют несколько блокировок; но важным моментом является то, что у нас не может быть потоков, использующих множественные блокировки с гарантиями, что они должны иметь .
Мы можем определить блокировку как тип вещи, которая имеет определенные операции, которые могут быть выполнены с ней, которые идут с определенными гарантиями. Одна гарантия: предположим, что существует объект блокировки x
, при условии, что каждый процесс, который вызывает lock(x)
, в конечном итоге вызывает unlock(x)
, любой вызов lock(x)
в конечном итоге будет успешно возвращен с x
, заблокированным текущим потоком / процесс. Эта гарантия значительно упрощает рассуждения о поведении программы.
К сожалению, если в мире более одной блокировки, это уже не так. Если поток A вызывает lock(x); lock(y);
, а поток B вызывает lock(y); lock(x);
, возможно, что A захватывает блокировку x
и B захватывает блокировку y
, и они оба будут бесконечно ждать, пока другой поток освободит другую блокировку: deadlock. Таким образом, блокировки не поддаются компоновке, потому что когда вы используете более одной, вы не можете просто утверждать, что эта важная гарантия все еще действует - , не анализируя код подробно, чтобы увидеть, как он управляет блокировками . Другими словами, вы больше не можете рассматривать функции как «черные ящики».
Вещи, которые можно компоновать, хороши тем, что они включают абстракций , что означает, что они позволяют нам рассуждать о коде, не заботясь обо всех деталях, и это уменьшает когнитивную нагрузку на программиста.