Ответ 1
Возможно, мысленный эксперимент немного устранит это расширение.
Предположим, что мы отбросили ограничение на то, что функции, определенные с помощью нескольких паттернов, должны находиться в одном месте, чтобы вы могли написать foo ("bar", Nothing) = ...
в верхней части модуля, а затем иметь такие случаи, как foo ("baz", Just x) = ...
в другом месте, Фактически, отпустите еще больше и разрешите, чтобы случаи были определены в разных модулях целиком!
Если вы думаете, что это звучит так, что это будет запутанным и подверженным ошибкам, вы правы.
Чтобы восстановить некоторое подобие здравомыслия, мы могли бы добавить некоторые ограничения. Например, (га, га), нам могут потребоваться следующие свойства:
- В любом случае такая функция используется, указанные аргументы должны соответствовать точно одному шаблону. Все остальное - ошибка компилятора.
- Добавление новых шаблонов (в том числе путем импорта другого модуля) никогда не должно изменять значение действительного кода - либо выбраны одинаковые шаблоны, либо возникает ошибка компилятора.
Должно быть ясно, что сопоставление простых конструкторов типа True
или Nothing
прост. Мы также можем немного переписать вещи и предположить, что компилятор может устранить ошибки, например "bar"
и "baz"
.
С другой стороны, связывание аргументов с шаблонами типа (x, Just y)
становится неудобным - запись такого шаблона означает отказ от возможности писать паттерны, такие как (True, _)
или (False, Just "foobar")
позже, поскольку это создаст неоднозначность. Хуже того, стражники становятся почти бесполезными, потому что им нужны очень общие матчи. Многие распространенные идиомы будут приводить к бесконечным двусмысленным головным болям, и, конечно, писать "дефолтный" паттерн полностью невозможно.
Это примерно такая же ситуация с экземплярами класса типов.
Мы могли бы восстановить некоторую выразительную силу, расслабив требуемые свойства как таковые:
- В любом случае такая функция используется, она должна соответствовать хотя бы одному шаблону. Нет совпадений - ошибка компилятора.
- Если используется функция, которая соответствует нескольким шаблонам, будет использоваться наиболее конкретный шаблон. Если нет уникального наиболее конкретного шаблона, возникает ошибка.
- Если функция используется таким образом, который соответствует общему экземпляру, но может применяться во время выполнения до аргументов, которые будут соответствовать более конкретному экземпляру, это ошибка компилятора.
Обратите внимание, что теперь мы находимся в ситуации, когда простое импортирование модуля может изменить поведение функции, введя в область новый более конкретный шаблон. В сложных случаях, в том числе и с функциями более высокого порядка, могут быть мутные. Тем не менее, во многих случаях проблемы маловероятны - скажем, определение общего падающего шаблона в библиотеке, позволяя клиентскому коду при необходимости добавлять конкретные случаи.
Это примерно где OverlappingInstances
ставит вас. Как было предложено в приведенном выше примере, если создание новых перекрытий всегда либо невозможно, либо желательно, и разные модули не смогут увидеть разные конфликтующие экземпляры, то это, вероятно, хорошо.
В чем дело, так это то, что ограничения, удаленные OverlappingInstances
, заключаются в том, что использование классов типов ведет себя разумно в соответствии с предположением "открытого мира" о возможном добавлении любого возможного экземпляра. Ослабляя эти требования, вы сами берете на себя это бремя; поэтому подумайте о том, как можно добавить новые экземпляры, и является ли какой-либо из этих сценариев серьезной проблемой. Если вы уверены, что ничто не сломается даже в неясных и хитрых угловых случаях, тогда продолжайте использовать расширение.