如何调用作为函数参数的函数值?
具体来说,我的目标是利用函数的参数,其中参数实际上是一个函数。
就我而言,我正在尝试实现一个用于记录数据的接口。
这是我的代码:
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
答案 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。