如何调用作为函数参数的函数值?

时间:2016-07-16 17:37:57

标签: f#

如何调用作为函数参数的函数值?

具体来说,我的目标是利用函数的参数,其中参数实际上是一个函数。

就我而言,我正在尝试实现一个用于记录数据的接口。

这是我的代码:

let logToFile (filePath:string) (message:string) =
    let file = new System.IO.StreamWriter(filePath)
    file.WriteLine(message)
    file.Close()

let makeInitialDeposit deposit =
    let balance = deposit |> insert []
    sprintf "Deposited: %f" balance

let logDeposit deposit (log:'medium ->'data -> unit) =
    deposit |> makeInitialDeposit 
            |> log

请注意以下功能:

let logDeposit deposit (log:'medium ->'data -> unit) =
    deposit |> makeInitialDeposit 
            |> log

我在日志函数上遇到编译错误:

  

这种结构导致代码的通用性低于   类型注释。类型变量'medium已被限制为   输入'string'。

我知道makeInitialDeposit返回字符串。 但是,该字符串类型将映射到泛型类型的数据。 因此,通用可以是任何类型的权利吗?

然后我尝试提供媒体(即文件)参数:

let logDeposit deposit (log:'medium ->'data -> unit) medium =
    deposit |> makeInitialDeposit 
            |> log medium

然后我的错误更新为:

  

这种结构导致代码的通用性低于   类型注释。类型变量'数据已被约束为   输入'string'。

我的目标

最终,我只想拥有一个名为log的接口,并传入该接口的实现(即logToFile)。

关于如何根据我的初步解释解释编译错误的任何指导?

插入功能依赖

let getBalance coins =
    coins |> List.fold (fun acc d -> match d with
                                     | Nickel         -> acc + 0.05
                                     | Dime           -> acc + 0.10
                                     | Quarter        -> acc + 0.25
                                     | OneDollarBill  -> acc + 1.00
                                     | FiveDollarBill -> acc + 5.00) 0.00

let insert balance coin =
    coin::balance |> getBalance

2 个答案:

答案 0 :(得分:2)

问题在于您已定义log :'medium ->'data -> unit

然后,您获取deposit |> makeInitialDeposit的结果,其类型为string并将其传递到log函数中。从逻辑上讲,编译器会推断出'medium = string

如果您在'medium函数中接受logDeposit参数,那么您只需沿着一个步骤移动该推断,deposit |> makeInitialDeposit仍然是string所以现在{{1} }。

我认为你很挣扎,因为这些功能不能很好地模拟你的域,你的日志记录逻辑正在逐渐渗透到你的其余代码中。

为什么'data = string会返回makeInitialDeposit

为什么string会返回getBalance,但float会接受insert作为Coin list参数?

我首先要创建一个接受三个参数的日志记录函数:

balance

类型为let logToFile (filePath:string) (formatf : 'data -> string) data = use file = new System.IO.StreamWriter(filePath) file.WriteLine(formatf data) data 。它接受一个登录路径,一个将类型filePath : string -> (formatf : 'data -> string) -> (data : 'data) -> data格式化为字符串的函数和一些'data来记录该文件。最后,它返回您提供的'data参数不变。这意味着您原则上可以在任何地方插入代码中任意值的记录。

然后我在域中设置了一些函数:

data

然后我可以使用这些函数,在任意点插入日志记录:

let valueOf = function
    | Nickel         -> 0.05m
    | Dime           -> 0.10m
    | Quarter        -> 0.25m
    | OneDollarBill  -> 1.00m
    | FiveDollarBill -> 5.00m

let totalValue coins =
    coins |> List.fold (fun acc coin -> acc + valueOf coin) 0.0m

let insert coins coin = coin::coins // returns Coin list

let makeInitialDeposit deposit = insert [] deposit // returns Coin list

这种方法可让您适应日志记录,而不是围绕日志记录构建域名。

答案 1 :(得分:1)

首先,您传递像"Deposited: 0.25"这样的字符串作为路径名称似乎很奇怪。您可能想要的是将message作为第一个参数,将filePath作为logToFile的第二个参数。因此log'data -> 'medium -> unit

解决这个问题,编译器错误的问题是makeInitialDeposit返回一个字符串,然后当你将结果传递给log时,就会发生约束。让它发挥作用的一种方法是向logDeposit添加另一个参数,将字符串转换为'data,如下所示:

let logDeposit deposit (log: 'data ->'medium-> unit) (stringConverter: string -> 'data) =
    deposit |> makeInitialDeposit
            |> stringConverter
            |> log

使用类似let dummyLog (a:int) (b:string) = ()之类的东西,假设你将适当的转换器从字符串传递给int。