避免使用Option.Value

Tea*_*Dev 3 f# idiomatic

我有这样的类型:

type TaskRow =
    {
        RowIndex : int
        TaskId : string
        Task : Task option
    }
Run Code Online (Sandbox Code Playgroud)

函数将返回这些记录的列表,以供进一步处理。有些这样做处理功能仅适用于有关TaskRow地方的项目Task是Some。我想知道最好的方法是什么。

天真的方法会这样做

let taskRowsWithTasks = taskRows |> Seq.filter (fun row -> Option.isSome row.Task)
Run Code Online (Sandbox Code Playgroud)

并将其传递给那些函数,只需假设Task它将永不存在,None并使用Task.Value,如果我不传递该特殊列表,就会冒NRE的风险。这正是当前的C#代码所做的,但是对于F#来说似乎很简单。我不应该“假设”事情,而应该让编译器告诉我什么将起作用。

更具“功能性”的是每次值相关时都进行模式匹配,然后对进行任何操作/不返回任何值(或使用choose类似方法)None,但这似乎是重复的和浪费的,因为同一工作将被重复执行多次。

另一个想法是引入第二种稍有不同的类型:

type TaskRowWithTask =
    {
        RowIndex : int
        TaskId : string
        Task : Task
    }
Run Code Online (Sandbox Code Playgroud)

然后,原始列表将被过滤到这种“子列表”中,以便在适当的情况下使用。从功能的角度来看,我认为这是可以的,但是我想知道是否有一种更好的,惯用的方式而不求助于这种“帮助类型”。

感谢您的指导!

Mar*_*ann 5

知道任务已经过过滤是很有价值的,因此拥有两种不同的类型可能会有所帮助。除了定义两个不同的类型(尽管在F#中没什么大不了的),您还可以考虑定义一个通用Row类型:

type Row<'a> = {
    RowIndex : int
    TaskId : string
    Item : 'a }
Run Code Online (Sandbox Code Playgroud)

这使您可以定义这样的投影:

let project = function
    | { RowIndex = ridx; TaskId = tid; Item = Some t } ->
        Some { RowIndex = ridx; TaskId = tid; Item = t }
    | _ -> None

let taskRowsWithTasks =
    taskRows
    |> Seq.map project
    |> Seq.choose id
Run Code Online (Sandbox Code Playgroud)

如果初始taskRows值具有类型seq<Row<Task option>>,则结果taskRowsWithTasks序列具有类型seq<Row<Task>>。

  • 为什么不使用`Seq.choose project`而不是`map` +`choose id`? (2认同)