2013-08-18 2 views
2

Можете ли вы объяснить, почему один работает, но не другой?Почему currying с obj не работает?

Учитывая

//fu : unit -> unit 
let fu() =();; 

Это работает

//exec : (unit -> unit) -> int -> unit 
let exec (f:(unit -> unit)) (data:int) = f();; 

//this works, and p : int -> unit 
let p = exec fu;; 

И это работает для других типов, таких как datastring, long и т.д.

Это не Работа

//exec : (unit -> unit) -> obj -> unit 
let exec (f:(unit -> unit)) (data:obj) = f();; 

let p = exec fu;; 

и я получаю следующее сообщение об ошибке:

error FS0030: Value restriction. The value 'p' has been inferred to have generic type val p : ('_a -> unit)
Either make the arguments to 'p' explicit or, if you do not intend for it to be generic, add a type annotation.

Обратите внимание, единственное различие между этими случаями является тип параметра data. Если obj или System.Object или 'a - не работает.

Другое дело, что если data имеет тип obj то происходит следующее:

//Data type is obj 
let exec (f:(unit -> unit)) (data:obj) = f();; 

//specifying parameters explicitly 
let p x = exec fu x;; 

Теперь p имеет подпись 'a -> unit, не obj -> unit.

Так что вопрос: почему «shortcuted» Выделка не работает, когда data является obj или 'a и почему тип p является 'a -> unit когда data был obj?

ответ

3

Так что я думаю, что проблема в том, что F #, как представляется, обобщая в неподходящий момент (с вашей точки зрения):

Вот версия кода, который на первый взгляд не должен typecheck:

let exec (f:unit -> unit) (data:obj) = f();; 
let p:int -> unit = exec (fun() ->());; 

Это кажется странным, как int <> obj.

Кроме того, здесь еще более простой пример, который показывает свое поведение (от спецификации с изменениями)

type Base() = 
    member b.X = 1 
type Derived(i : int) = 
    inherit Base() 
    member d.Y = i 
let exec (f:unit -> unit) (data:Base) = f();; 
let p = exec (fun() ->());; 

, который выдает ошибку ограничения значения.

Это делает более понятным то, что, поскольку F # вставляет неявный результат перед вызовом функции, код действителен там, где он является функцией, но после того, как вы сделаете это явным значением, этот прилив не может быть использован.

Если вы хотите, чтобы ваш код для компиляции, вам необходимо переместить расположение вентиляционной шахты, добавив аннотацию типа:

let exec (f:unit -> unit) (data:obj) = f() 
let p:obj -> unit = exec (fun() ->());; 
+0

Так я предполагаю, что это ошибка или ограничение умозаключения # типа F?Потому что для меня у компилятора достаточно информации для обобщения на то, что может работать, скажем, вообще не изменять тип параметра 'data'. –

+0

@AlexeyRaga - Я думаю, что лучше всего рассматривать это как ограничение вывода F #. –