Это неловкое скелет в шкафу F # s.
Попробуйте это:
> let mapPair f (x,y) = (f x, f y)
val mapPair : f:('a -> 'b) -> x:'a * y:'a -> 'b * 'b
Полностью универсальный! Очевидно, что применение функции и кортежи работают.
Теперь попробуйте это:
> let makeList a b = [a;b]
val makeList : a:'a -> b:'a -> 'a list
Хммм, а также общий характер. Как об этом:
> let makeList a b = [a + b]
val makeList : a:int -> b:int -> int list
Ага, как только у меня есть (+)
там, он становится int
по какой-то причине.
Давайте продолжать играть:
> let inline makeList a b = [a + b]
val inline makeList :
a: ^a -> b: ^b -> ^c list
when (^a or ^b) : (static member (+) : ^a * ^b -> ^c)
Хммм, интересно. Оказывается, если я делаю функцию inline
, тогда F # делает, считая его общим, но также дает это странное предложение when
, а мои общие параметры имеют этот странный символ ^
вместо обычного тика.
Этот странный синтаксис называется «статически разрешенные параметры типа» (см. here для несколько согласованного объяснения), а основная идея состоит в том, что для функции (+)
требуются аргументы для определения static member (+)
.Давайте проверим:
> let x = 0 :> obj
let y = 0 :> obj
let z = x + y
Script1.fsx(14,13): error FS0001: The type 'obj' does not support the operator '+'
> type My() =
static member (+)(a:My, b:My) = My()
let x = My()
let y = My()
let z = x + y
val x : My
val y : My
val z : My
Теперь проблема состоит в том, что CLR не поддерживает такого рода общих параметров (например, «любой тип, до тех пор, как у него есть такие и такие члены»), так что F # должен fake и разрешить эти вызовы во время компиляции. Но из-за этого любые методы, которые используют эту функцию, не могут быть скомпилированы в истинные общие IL-методы и, следовательно, должны быть мономорфными (что включено inline
).
Но тогда было бы очень неудобно требовать, чтобы каждая функция, использующая арифметические операторы, была объявлена inline
, не так ли? Таким образом, F # выполняет еще один дополнительный шаг и пытается исправить эти статически разрешенные общие параметры на основе того, как они создаются позже в коде. Вот почему ваша функция превращается в string->string->string
, как только вы используете ее с string
один раз.
Но если вы отмечаете вашу функцию inline
, F # не придется фиксировать параметры, потому что он не должен был бы составить функцию вплоть до IL, и поэтому ваши параметры остаются неизменными:
> let inline add a b = a + b
val inline add :
a: ^a -> b: ^b -> ^c
when (^a or ^b) : (static member (+) : ^a * ^b -> ^c)
Я представляю что это дубликат; Я был бы очень удивлен, если бы его не спрашивали раньше. Но это отличное объяснение проблемы. – phoog