我想为受歧视的联合定义一个默认值,如下所示:
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
let result = formatter.string(from: date)
self.backS.sendLocation(msg: "data fetched \(result)")
选项<'一>以这种方式工作(firstResult将是None),所以它应该是可能的。 谢谢你的帮助。
编辑: 我正在使用SQLDataProvider,并希望编写像
这样的代码"2018-05-12 14:34:51 +0000"
我的实际结果类型如下所示:
open System.Linq
type Result =
| Ok
| Error
let results : seq<Result> = [] |> Seq.ofList
let firstResult = results.FirstOrDefault()
// I want firstResult to be Error, currently it is null.
返回选项后,调用者将无法区分失败和错误。
答案 0 :(得分:7)
通常,F#避免使用默认值的概念,而是使所有内容尽可能明确。返回一个选项并让调用者决定特定用例的默认值是更惯用的。
FirstOrDefault
方法只能为任何类型返回.NET的默认值。因此,对于任何类,它将返回null
,对于数字,它将返回零。
我建议您使用此方法,而不是假设您所需的默认值为Ok
:
results |> Seq.tryHead |> Option.defaultValue Ok
答案 1 :(得分:4)
您可以使用UseNullAsTrueValue
编译参数执行此操作。这告诉编译器使用null
作为其中一个无参数情况的内部表示。这有点像黑客(因为这主要是为了性能优化,在这里我们有点滥用它,但它可能会有效)。
我没有SQL数据库设置来尝试使用SQL提供程序,但以下工作在内存中,我认为它也适用于SQL:
[<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]
type Result<'a> =
| Ok of 'a
| Failure of string
| Error
let userEmail =
query {
for a in [1] do
where (a = 2)
select (Ok a)
headOrDefault }
如果你运行这个,F#interactive会打印出userEmail = null
,但那很好,以下是真的:
userEmail = Error
答案 2 :(得分:2)
如果要表示数据或查询参数的问题(例如,未查找特定记录)与其他类型的故障不同,例如未连接到数据库或遇到未处理的异常,则可以创建一个有区别的联合,明确表示您的预期数据/查询问题。然后,您可以返回该DU而不是string
作为Error
案例的数据,并且您不会真正需要Failure
案例。考虑这样的事情:
type SqlError =
| NoMatchingRecordsFound of SqlParameter list
| CouldNotConnectToDatabase
| UserDoesNotHaveQueryPermissions
| UnhandledException of exn
我建议您查看Railway-Oriented Programming并遵循为您的不同错误案例定义歧视联盟的模式。您仍然可以在该DU中包含错误消息,但我建议对所有不同的预期失败使用显式情况,然后使用&#34; UnhandledException&#34;您的意外错误或类似情况,可能是数据exn
。
如果您有兴趣,我会在GitHub / NuGet上设置一个库,将所有ROP内容放在一起,并增加与Task
,Async
的互操作性和一个计算构建器中的Lazy
类型,因此您不必将所有代码包含在您自己的项目中。
修改强>
这是一个完整的ROP风格示例(使用链接框架):
open FSharp.Control
open System.Linq
// Simulate user table from type-provider
[<AllowNullLiteral>]
type User() =
member val Id = 0 with get,set
member val Name = "" with get,set
// Simulate a SQL DB
let users = [User(Id = 42, Name = "The User")].AsQueryable()
// Our possible events from the SQL query
type SqlEvent =
| FoundUser
| UserIdDoesNotExist of int
| CouldNotConnectToDatabase
| UnhandledException of exn
// Railway-Oriented function to find the user by id or return the correct error event(s)
let getUserById id =
operation {
let user =
query {
for user in users do
where (user.Id = id)
select user
headOrDefault
}
return!
if user |> isNull
then Result.failure [UserIdDoesNotExist id]
else Result.successWithEvents user [FoundUser]
}
Caling getUserById 42
将返回与用户和事件FoundUser
成功完成的操作。为任何其他号码(例如0)调用getUserById
将返回错误事件UserIdDoesNotExist 0
的失败操作。您可以根据需要添加更多活动。