F# 计算表达式与应用函子的比较
不完全确定标题是否描述了它,但我确实了解以下代码: paket.dependencies:F# 计算表达式与应用函子的比较,f#,monads,computation-expression,fsharpx,F#,Monads,Computation Expression,Fsharpx,不完全确定标题是否描述了它,但我确实了解以下代码: paket.dependencies: source https://www.nuget.org/api/v2 nuget fsharpx.extras nuget mongodb.driver some.fsx: #r @".\packages\MongoDB.Bson\lib\net45\MongoDB.Bson.dll" #r @".\packages\MongoDB.Driver\lib\net45\MongoDB.Driver.dl
source https://www.nuget.org/api/v2
nuget fsharpx.extras
nuget mongodb.driver
some.fsx:
#r @".\packages\MongoDB.Bson\lib\net45\MongoDB.Bson.dll"
#r @".\packages\MongoDB.Driver\lib\net45\MongoDB.Driver.dll"
#r @".\packages\MongoDB.Driver.Core\lib\net45\MongoDB.Driver.Core.dll"
#r @".\packages\FSharpX.Extras\lib\net45\FSharpX.Extras.dll"
open MongoDB
open MongoDB.Driver
open MongoDB.Bson
open MongoDB.Bson.Serialization
open FSharpx.Choice
let private createClient (connectString:string) = MongoClient(connectString)
let CreateClient = protect createClient
let private getDb name (client:IMongoClient) = client.GetDatabase(name)
let GetDB1 name client =
choose {
let! c = client
return! (protect (getDb name) c)
}
let GetDB2 name (client:Choice<IMongoClient, exn>) =
protect (getDb name)
<!> client
#r@.\packages\MongoDB.Bson\lib\net45\MongoDB.Bson.dll
#r@“\packages\MongoDB.Driver\lib\net45\MongoDB.Driver.dll”
#r@“\packages\MongoDB.Driver.Core\lib\net45\MongoDB.Driver.Core.dll”
#r@“\packages\FSharpX.Extras\lib\net45\FSharpX.Extras.dll”
开放MongoDB
打开MongoDB驱动程序
打开MongoDB.Bson
打开MongoDB.Bson.Serialization
打开FSharpx.Choice
让私有createClient(connectString:string)=MongoClient(connectString)
让CreateClient=保护CreateClient
让私有getDb名称(客户端:IMongoClient)=client.GetDatabase(名称)
让GetDB1命名客户端=
选择{
设!c=客户机
return!(protect(getDb name)c)
}
让GetDB2命名(客户机:选择)=
保护(getDb名称)
客户
这个“练习”的要点是编写GetDB2,使其与GetDB1相同,但使用操作符(applicatives?),但目前我还不能回头来管理它
上面的代码可以编译,但是
GetDB1和GetDB2不相等,我显然做了一些不正确的事情
val GetDB1 :
name:string ->
client:Choice<#MongoDB.Driver.IMongoClient,exn> ->
Choice<MongoDB.Driver.IMongoDatabase,exn>
val GetDB2 :
name:string ->
client:Choice<MongoDB.Driver.IMongoClient,exn> ->
Choice<Choice<MongoDB.Driver.IMongoDatabase,exn>,exn>
val GetDB1:
名称:字符串->
客户:选择->
选择
val GetDB2:
名称:字符串->
客户:选择->
选择
我在GetDB2中尝试了几个版本和操作顺序,但我或多或少都会以上面的签名结束
我最初的想法是编写一些小函数来完成它们应该做的事情,然后添加异常处理(protect),然后相应地“wrap”和“unwrap”
这当然也可能不是完全正确的想法
有人能在这里为我指出一些进一步研究、代码示例或其他方面的方向吗?在这一点上,任何类型的评论实际上都是受欢迎的;-)
附录
我认为以下内容应该与上面的内容大致相同,但是没有mongodb依赖项
#r @".\packages\FSharpX.Extras\lib\net45\FSharpX.Extras.dll"
type DataBase =
{
Name: string
}
type Client =
{
connectString: string
} with member this.GetDatabase name = {
Name = name
}
open FSharpx.Choice
let private createClient (connectString:string) = {
connectString= connectString
}
let CreateClient = protect createClient
let private getDb name (client:Client) = client.GetDatabase name
let GetDB1 name client =
choose {
let! c = client
return! (protect (getDb name) c)
}
let GetDB2 name client =
protect (getDb name)
<!> client
#r@.\packages\FSharpX.Extras\lib\net45\FSharpX.Extras.dll
类型数据库=
{
名称:string
}
类型客户端=
{
连接字符串:字符串
}使用成员this.GetDatabase名称={
Name=Name
}
打开FSharpx.Choice
让私有createClient(connectString:string)={
connectString=connectString
}
让CreateClient=保护CreateClient
让私有getDb名称(客户端:client)=client.GetDatabase名称
让GetDB1命名客户端=
选择{
设!c=客户机
return!(protect(getDb name)c)
}
让GetDB2命名客户端=
保护(getDb名称)
客户
您在这里得到的是类型的复合,因为您使用了
操作符,即map
。定义如下:
let map f = function
| Choice1Of2 value = Choice1Of2 (f value)
| Choice2Of2 fail = Choice2Of2 fail
let bind f = function
| Choice1Of2 value = f value
| Choice2Of2 fail = Choice2Of2 fail
let protect f x =
try
Choice1Of2 (f x)
with
exn -> Choice2Of2 exn
它具有签名('T->'U)->Choice->Choice
,即函数f
用作Choice
类型内的映射。例如:
map (sprintf "%d")
具有类型选项
。这对于应用不使用选项
类型的函数很有用-只有一个可能的故障点,并且在调用映射
之前发生
但是,您的下一个函数将生成一个选项
类型,但它将使用一个非选项
类型。这意味着您希望错误通过传播-如果值中存在错误,请选择该值。如果该值很好,但函数中有错误,则使用该值。如果一切都成功了,就用它。这要求两种错误类型相同,对您来说是(exn
)
这是对绑定操作的描述,定义如下:
let map f = function
| Choice1Of2 value = Choice1Of2 (f value)
| Choice2Of2 fail = Choice2Of2 fail
let bind f = function
| Choice1Of2 value = f value
| Choice2Of2 fail = Choice2Of2 fail
let protect f x =
try
Choice1Of2 (f x)
with
exn -> Choice2Of2 exn
签名('T->Choice)->Choice->Choice
请注意,bind
与map
非常相似,只是后者将结果提升到选项1of2
-映射函数总是成功的
在FSharpX中,您可以通过|>
-like操作符>=
或'T->Choice
访问绑定
,因为映射
在选项
中工作
不过,你想要的是
let GetDB name client =
bind (protect (getDb name)) client
或者以操作员的形式
let GetDB name client = client >>= protect (getDb name)
通常,如果映射函数具有签名
'T->'U
,则需要map
。如果它有'T->Choice
,您需要bind
您可以将其表示为一个?我不想为了研究这个问题而去摆弄MongoDB……顺便说一句,在F#
中,Haskell的
经常被用来代替,后者在F#中不是合法的操作员。它只是Haskell中map
(fmap
)的中缀版本。@MarkSeemann-hehe。事实上是MCVE。或者说:不需要在这里摆弄mongos;-)如果pakets已安装到位,则上述操作将在未安装任何mongodb或未进行任何修改的情况下运行。但是我会尝试对bone MCVE做更多的工作…我想你需要类似于(=我明白了。我不知道我是否能给出有用的解释,但是>=
只是“只是”标准绑定操作符,它与返回/纯一起定义了一个单子。在F#计算表达式中。