ну тут вопрос о том, что первично а что вторично.
В моем мире контракт функции первичен - она определяет, что она умеет работать с 1, 2 но не с 3. И функции которые её вызывают должны это учитывать, а она соответственно может матчить только 2 возможных варианта.
А если получается наоборот, мы дергаем функцию с разными аргументами, а потом внутри функции знаем, что нас вызывали с 3,5,7 и 12 и мы должны их как-то обработать, получается что caller - главный, а callee под него подстравивается. Кмк это неудобно, потому что во-первых размазывает зону ответственности функции, её скоуп теперь не только сигнатуры + тело, но ещё и все возможные способы обработки. Во-вторых не представляю как это должно работать при например динамическом связаывании, когда наша функция лежит в dll и мы никак не можем знать кто с какими аргументами её использует
Да, это два подхода. Но здесь нужно понимать, что первый подход продиктован именно ограничением базовой модели. Все эти выводы типов академические мономорфны и на полиморфизм их расширили только номинально - назвав им подтипы.
Поэтому мы не можем говорить о каком-то сравнении, когда у нас нет возможности сравнивать. Это фундаментальная проблема. Чтобы выбирать - нужно иметь возможность выбирать. Если её нет, то любой выбор не более чем необходимость, а всё остальное - оправдание. И неважно правильный он или нет - он в любом случае обусловлен не этим.
По поводу контракта. Здесь всё не совсем так. Именно функция описывает свой контракт, той логикой что в ней описано - это набор операций. Это тот самый структурный подход. И ей абсолютно неважно какие там типы, какие входы, какие операции. Как эти операции определены для типов.