F#类型在curried函数上不匹配

时间:2016-02-22 12:11:30

标签: f#

我在使用以下FSharp / 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 []但我希望能够返回一个字符串。

我认为我错过了一些通用声明的技巧,但是经过几个小时的代码移动后,我似乎无法解决这个问题。

(我已经简化了代码以突出我的问题)

2 个答案:

答案 0 :(得分:2)

错误是因为ops需要在编译时解析其handler的返回类型,并且您希望根据某些运行时逻辑返回不同的类型。

它基本上相当于:

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

你想以这种方式运行它:

fun1 true 1 "string"

当然,arg2和arg3需要具有相同的类型,因此它不会起作用

你可以做的是运行" api"函数处理程序结果,在返回之前(所以它总是相同的类型 - 单位)。

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

或者,你可以返回有区别的联合类型的对象(那么在api函数中你需要更多的逻辑)。

(从技术上讲,您也可以返回obj)。

<强>加成

您不需要在router函数中返回单位数组,返回一个单位就可以了:

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

通过这种方式,setup函数也会返回unit,您无需在结果上运行ignore就可以运行它来摆脱This expression should have type 'unit', but has type 'unit[]'警告。

答案 1 :(得分: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