2015-05-25 4 views
10

Я запутался, как маркировать функцию как родовое без декларации явного типа, как ('a -> 'a)F # дженерики/функция перегрузки синтаксиса

let add a b = a + b 

Это дает нам

val add : a:int -> b:int -> int 

Однако мы можем немедленно звоните

add "Hello " "World!" 

и теперь значение оных является

val add : a:string -> b:string -> string 
val it : string = "Hello World!" 

Если мы тогда называем

add 2 3 // then we get 
error: This expression was expected to have type string but here has type int 

Как убедиться, что функция работает на всех типах, которые говорят, есть функция (+) определена

+0

Я представляю что это дубликат; Я был бы очень удивлен, если бы его не спрашивали раньше. Но это отличное объяснение проблемы. – phoog

ответ

16

Это неловкое скелет в шкафу 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) 
+0

«Это неудобный скелет F # в шкафу». -- единственный? – phoog

+0

Уверен, что их несколько, но я не могу вспомнить ни одного из них. –

2

Если я вас правильно понимаю, использовать встроенный:

let inline add a b = a + b 

add 2 3 |> printfn "%A" 
add "Hello " "World!" |> printfn "%A" 

Печать:

5 
"Hello World!" 

Ссылка: http://ideone.com/awsYNI

2

Сделать inline

let inline add a b = a + b 
(* 
val inline add : 
    a: ^a -> b: ^b -> ^c 
    when (^a or ^b) : (static member (+) : ^a * ^b -> ^c) 
*) 
add "Hello " "World!" 
// val it : string = "Hello World!" 
add 2 3 
// val it : int = 5 
Смежные вопросы