2016-02-22 4 views
3

У меня немного проблем со следующей FSharp/F # кодом:F # Тип Несоответствие кэррите функции

module File1 

let api a = 
    printf ("VALUE = %A") a 

let router ops = 
    [| 
     api (ops (fun (list, _) -> list())) 
     api (ops (fun (_, get) -> get 1)) 
    |] 

let withContext ops handler = 
    let context = "CONTEXT" 
    handler (ops context) 

let operations context = 
    printf ("CONTEXT = %s") context 

    let list() = [|1;2;3|] 
    let get id = "Test" 
    (list, get) 

let setup() = 
    let ops = withContext operations 
    router ops 

Результатов в следующей ошибке

Results in the following compation error 
Error 1 Type mismatch. Expecting a 
    ((unit -> int []) * (int -> int []) -> int []) -> 'a  
but given a 
    ((unit -> int []) * (int -> string) -> 'b) -> 'b  
The type 'int []' does not match the type 'string' 

Я знаю, что проблема заключается в том, что функция ops обязана возвращать int [], но я хочу также иметь возможность возвращать строку.

Я думаю, что мне не хватает трюка с некоторыми общими декларациями, но после нескольких часов движения кода я не могу работать.

(я упростил код, чтобы выделить мою проблему)

+0

Всего несколько замечаний: '' [| ... |] '' - это массив, а '' [...] '' - это список. '' api'' должен быть '' 'T -> unit'', правильно? Поэтому '' router ops'' должен быть '' unit [] '', массив единиц. Это то, что вы хотите? – BitTickler

+0

Как сделать много типов в 1 тип? Дискриминационные союзы.'' type OpKinds = | Массив из int -> int [] | Строка int -> string'' – BitTickler

+0

Это будет частью приложения suave.io, поэтому типы возвращаемых данных могут быть любым числом, поэтому я не уверен, что попытка создания огромного списка дискриминационных союзов будет работать. – davidtme

ответ

2

Ошибка в том, что ops должен иметь тип возвращаемого из его handler решена при компиляции, и вы хотите, чтобы возвращать различные базы на некоторых логики времени выполнения.

Это в основном эквивалент:

let fun1 switch arg2 arg3 = 
    if switch then 
    arg2 
    else 
    arg3 

и вы хотите запустить его таким образом:

fun1 true 1 "string" 

Конечно, arg2 и Арг3 должны иметь тот же тип, поэтому он выиграл 't work

Что вы можете сделать, так это запустить функцию «api» на результат обработчика, прежде чем возвращать его (так что он будет всегда одного и того же типа).

let router ops = 
    [| 
     ops (fun (list, _) -> api <| list())) 
     ops (fun (_, get) -> api <| get 1)) 
    |] 

В качестве альтернативы, вы можете возвращать объекты размеченного типа союза (то вам нужно больше логики в функции API).

(Технически вы также можете вернуть obj).

Bonus

Вам не нужно массив единиц, которые будут возвращены в router функции, возвращая один блок просто отлично:

let router ops = 
    ops (fun (list, _) -> api <| list())) 
    ops (fun (_, get) -> api <| get 1)) 

Таким образом, setup функция также будет return unit, и вы сможете запустить его без необходимости запускать ignore на результат, чтобы избавиться от предупреждения This expression should have type 'unit', but has type 'unit[]'.

+0

Коммутатор потребует знания всех разных типов возврата и может закончиться как большая монолитная функция – davidtme

+0

Перемещение функции api означает, что все теперь привязано к единице, это компилируется, но не решает проблему. Если бы мне пришлось расходовать пример, то функция ops будет подключаться к базе данных, запускать запрос, а затем удалять соединение, результат которого будет затем сериализован api – davidtme

+0

@ davidtme Если я понимаю, что вы хотите сделать правильно, совет будет искать «поставщиков типов» и как их создавать. F # - статически типизированный язык, и поэтому вы не можете изобретать новые типы во время выполнения (в зависимости от определения таблицы базы данных, например). Чтобы создать иллюзию этого, были предложены типы поставщиков. – BitTickler

1

Ваш код мне трудно понять, но я думаю, что основная проблема заключается в том, что вы хотите, чтобы withContext имел тип «ранг 2» (так что универсальное количественное определение переменной типа 'b может произойти после применения первый аргумент). В F # это может быть выполнено путем создания нового типа с общим методом и с использованием этого:

let api a = 
    printf ("VALUE = %A") a 

type Handler<'a> = abstract Handle<'b> : f:('a->'b) -> 'b 

let router (ops:Handler<_>) = 
    [| 
     api (ops.Handle (fun (list, _) -> list())) 
     api (ops.Handle (fun (_, get) -> get 1)) 
    |] 
let withContext ops = 
    let context = "CONTEXT" 
    { new Handler<_> with member __.Handle f = f (ops context) } 

let operations context = 
    printf ("CONTEXT = %s") context 

    let list() = [|1;2;3|] 
    let get id = "Test" 
    (list, get) 

let setup() = 
    let ops = withContext operations 
    router ops 
+0

Мне очень нравится этот подход, это означает, что ничего не нужно бросать в объект – davidtme

Смежные вопросы