F# 如何调用用作函数参数的函数值?

F# 如何调用用作函数参数的函数值?,f#,F#,如何调用用作函数参数的函数值? 具体来说,我的目标是利用函数的一个参数,其中的参数实际上是一个函数 在我的例子中,我试图实现一个用于记录数据的接口 这是我的代码: let logToFile (filePath:string) (message:string) = let file = new System.IO.StreamWriter(filePath) file.WriteLine(message) file.Close() let makeInitialDepo

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

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

在我的例子中,我试图实现一个用于记录数据的接口

这是我的代码:

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
let logDeposit deposit (log:'medium ->'data -> unit) medium =
    deposit |> makeInitialDeposit 
            |> log medium
注意以下功能:

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
let logDeposit deposit (log:'medium ->'data -> unit) medium =
    deposit |> makeInitialDeposit 
            |> log medium
我在日志函数中遇到编译错误:

此构造导致代码不像 类型注释。类型变量“medium”已被约束为 键入“string”

我知道makeInitialDeposit返回字符串。 但是,该字符串类型映射到泛型类型“数据”。 因此,泛型可以是任何类型,对吗

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

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
let logDeposit deposit (log:'medium ->'data -> unit) medium =
    deposit |> makeInitialDeposit 
            |> log medium
然后我的错误被更新为:

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
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

首先,您传入一个字符串,如
“deposition:0.25”
作为路径名,这似乎有些奇怪。您可能希望将
message
作为第一个参数,将
filePath
作为
logToFile
的第二个参数。相应地,
log
将成为
'data->'medium->unit

要解决这个问题,编译器错误的问题是
makeInitialDeposit
返回一个字符串,然后将该结果导入
log
时,约束就会发生。使其工作的一种方法是向
logDeposit
添加另一个参数,以将字符串转换为
数据,如下所示:

let logDeposit deposit (log: 'data ->'medium-> unit) (stringConverter: string -> 'data) =
    deposit |> makeInitialDeposit
            |> stringConverter
            |> log
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

使用类似于
的方法,假设您传入从字符串到int的适当转换器,让dummyLog(a:int)(b:string)=()
可以工作。

首先,您传入一个类似于
的字符串作为路径名似乎有些奇怪。您可能希望将
message
作为第一个参数,将
filePath
作为
logToFile
的第二个参数。相应地,
log
将成为
'data->'medium->unit

要解决这个问题,编译器错误的问题是
makeInitialDeposit
返回一个字符串,然后将该结果导入
log
时,约束就会发生。使其工作的一种方法是向
logDeposit
添加另一个参数,以将字符串转换为
数据,如下所示:

let logDeposit deposit (log: 'data ->'medium-> unit) (stringConverter: string -> 'data) =
    deposit |> makeInitialDeposit
            |> stringConverter
            |> log
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

使用类似于
的方法,假设您将适当的转换器从字符串传递到int,让dummyLog(a:int)(b:string)=()
可以工作。

问题是您已经定义了
日志:'medium->'数据->单元

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

如果在
logDeposit
函数中接受一个
'medium
参数,则只需将该推断沿着一个步骤移动,
deposit |>makeInitialDeposit
仍然是一个
字符串
,因此现在
'data=string


我认为您正在挣扎,因为这些函数不能很好地为您的域建模,您的日志逻辑正在泄漏到代码的其余部分

为什么
makeInitialDeposit
返回
字符串

为什么
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
。最后,它返回您提供的未更改的
数据
参数。原则上,这意味着您可以在代码的任何位置插入任意值的日志记录

然后,我在域中设置了一些函数,如下所示:

let logDeposit deposit (log: 'data ->'medium-> unit) (stringConverter: string -> 'data) =
    deposit |> makeInitialDeposit
            |> stringConverter
            |> log
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
然后,我可以使用这些函数,在任意点插入日志:

let balance =
    makeInitialDeposit OneDollarBill
    |> logToFile "file.txt" (sprintf "Initial Deposit: %A")
    |> totalValue
    |> logToFile "file2.txt" (sprintf "Balance : $%M")

这种方法使您可以在域中进行日志记录,而不是围绕日志记录构建域。

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

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

如果在
logDeposit
函数中接受一个
'medium
参数,则只需将该推断沿着一个步骤移动,
deposit |>makeInitialDeposit
仍然是一个
字符串
,因此现在
'data=string


我认为您正在挣扎,因为这些函数不能很好地为您的域建模,您的日志逻辑正在泄漏到代码的其余部分

为什么
makeInitialDeposit
返回
字符串

为什么
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
它的类型