Во-первых, обратите внимание, что этого не произойдет, если мы объявим mul
как встроенную функцию:
let inline mul x y = x * y
let mul1 = mul 1 2 // works
let mul2 = mul 1.1 2.2 // also works
Здесь предполагаемый тип mul
будет выглядеть следующим образом:
x: ^a -> y: ^b -> ^c
when ( ^a or ^b) : (static member ( * ) : ^a * ^b -> ^c)
Этот тип означает, что параметры x
и y
могут иметь любой тип (даже не обязательно того же типа), если хотя бы у одного из них есть статический член с именем *
, который принимает аргументы тех же типов, что и x
и y
. Тип возврата mul
будет таким же, как и для элемента *
.
Так почему же вы не получаете такое же поведение, когда mul
не встроен? Поскольку ограничения членов (то есть ограничения типов, которые говорят, что у типа должны быть определенные члены) разрешены только для встроенных функций - именно поэтому переменные типа имеют ^
впереди вместо обычного '
: чтобы показать, что мы ' мы имеем дело с другим, менее ограниченным типом переменной типа.
Так почему же существует это ограничение для не встроенных функций? Из-за чего .NET поддерживает. Ограничения типа, такие как «T реализует интерфейс I», выражаются в байт-коде .NET и, таким образом, разрешены во всех функциях. Ограничения типа, такие как «T должен иметь определенный член с именем X с типом U», не могут быть выражены и поэтому не допускаются в обычных функциях. Поскольку встроенные функции не имеют соответствующего метода в сгенерированном байт-коде .NET, нет необходимости, чтобы их тип был выразим в байт-коде .NET, и поэтому ограничения к ним не применяются.