Avoiding using Option.Value

I have a type like this:

type TaskRow =
    {
        RowIndex : int
        TaskId : string
        Task : Task option
    }

      

The function returns a list of these records for further processing. Some of the functions that perform this processing are only applicable to items TaskRow

where Task

- Some

. I am wondering what is the best way to do this.

Naive way:

let taskRowsWithTasks = taskRows |> Seq.filter (fun row -> Option.isSome row.Task)

      

and passing it on to these functions, just assuming it Task

never will None

and using Task.Value

it at the risk of NRE if I don't go to this special list. This is exactly what the current C # code does, but seems pretty uniomatic for F #. I don't have to "accept" things, but let the compiler tell me what will work.

More "functional" would be to match patterns every time it matters, and then do / return nothing (and use choose

or the like) for None

, but this seems repetitive and wasteful since the same work is done multiple times.

Another thought was to introduce a second, slightly different type:

type TaskRowWithTask =
    {
        RowIndex : int
        TaskId : string
        Task : Task
    }

      

The original list will then be filtered into "subscriptions" of this type, which will be used where needed. I think that would be fine from a functional point of view, but I'm wondering if there is a nicer, idiomatic way without resorting to a "helper type" like this.

Thanks for any pointers!

+3


source to share


2 answers


There is quite a bit of information here, knowing that tasks are already filtered out, so using two different types can be helpful. Instead of defining two different types (which is not a big deal in F #), you might also consider defining a type Row

:

type Row<'a> = {
    RowIndex : int
    TaskId : string
    Item : 'a }

      

This allows you to define the projection as follows:



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

      

If the original value taskRows

is of type seq<Row<Task option>>

, then the resulting sequence taskRowsWithTasks

is of type seq<Row<Task>>

.

+4


source


I agree with you that the more "purely functional" way is to repeat pattern matching, I mean using a function c Seq.choose

that does the filtering instead of storing it in a different structure.

let tasks = Seq.choose (fun {Task = t} -> t) taskRows

      



The problem is performance, because it will be evaluated many times, but you can use it Seq.cache

, so behind the scenes it is stored in an intermediate structure, while keeping your code more "functional clean".

+3


source







All Articles