Pregunta

When retrieving unique items from a database I need to cater for the scenario when there is no data for the ID provided by the client. For example, the ID is incorrect or the cached data has expired.

The particular DB client library I am using in this example is ServiceStack.Redis, but I think the principle applies to any CLR library.

I have defined my data structure using a record type shown below. Now, when I use the client library to retrieve data for a key that does not exist in the database a null value is returned. This, I expect and am happy with. The problem is that the F# compiler will not let me pattern match for this scenario - even though it can happen at runtime!

type MyRecordType = { id:int; name:string; desc:string }

let redis = new RedisClient("localhost")
let nullCheck =
    let item = redis.Get<MyRecordType> "xxx"
    // it is possible that item is null
    // but the compiler will not permit the match
    match item with
    | null -> None
    | _ -> Some item

The Redis client library includes a 'ContainsKey' method returning a boolean which I could use first but two calls to the database is unnecessary here. The other way around is to use a class defined in a C# project as the data structure, but that involves too much overhead. The attribute also does no permit a match on null.

I want to know what convention I should use to deal with this scenario as it seems like a very common problem?

¿Fue útil?

Solución

Unfortunately you can't use AllowNullLiteral on records. As a result, the best solution is to just create a null value and do an equality check like so

if item = Operators.Unchecked.defaultof<_> then None else Some(item)

Otros consejos

I also found this article which makes use of boxing. I think it reads slightly cleaner:

if (box item = null) then None else Some item

I think this might perform a bit better than the other options I've seen. The F# equals operator is very powerful, but is also quite a bit of overkill when all you really want to know is whether you have a null reference.

if obj.ReferenceEquals(item, null) then None else Some item

It may not matter unless you're doing a lot of comparisons, but there is a real difference:

> for i = 1 to 100000000 do bar = null |> ignore;;
Real: 00:00:00.233, CPU: 00:00:00.234, GC gen0: 0, gen1: 0, gen2: 0
val it : unit = ()
> for i = 1 to 100000000 do obj.ReferenceEquals(bar, null) |> ignore;;
Real: 00:00:00.030, CPU: 00:00:00.031, GC gen0: 0, gen1: 0, gen2: 0
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top