Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
What does F#'s `box` keyword do and where is it documented?
The Null Values article in the F# Language Reference show an example that uses it, but it does not explain what it does exactly.
You can use the following code to check if an arbitrary value is
null
.match box value with | null -> printf "The value is null." | _ -> printf "The value is not null."
I went through the entire PDF version of the language reference, but there is not trace of it ever being introduced.
1 answer
box
and unbox
are operators documented in the F# Core API Reference; it tersely states that they box / unbox (respectively) a strongly typed value.
-
box a
returns valuea
"wrapped" in a .NETSystem.Object
instance"wrapped" here simply means that
a
is upcast toSystem.Object
:type Lofa = | A | B match A with | :? Lofa -> A //=> error FS0016 match (box A) with | :? Lofa -> A //ok match (A :> obj) | :? Lofa -> A //ok match (A :> System.Object) | :? Lofa -> A //ok
-
unbox<a_type> a
will try to unwrap valuea
and cast it into ana_type
or throw an exception otherwise
For completeness' sake, there is also a tryUnbox
operator, that can be called the same way as unbox
, but it returns the boxed value wrapped in an Option
.
Example use cases
1. Type detection during runtime
The F# for Fun and Profit article Built-in .NET types1 has an entire section dedicated to box
and unbox
which ends with the following example:
[1]: That is, types of the Common Language Infrastructure (CLI).
Let’s say that you want to have a function that matches based on the type of the parameter, using the
:?
operator:let detectType v = match v with | :? int -> printfn "this is an int" | _ -> printfn "something else"
Unfortunately, this code will fail to compile, with the following error:
// error FS0008: This runtime coercion or type test // from type 'a to int involves an indeterminate type // based on information prior to this program point. // Runtime type tests are not allowed on some types. // Further type annotations are needed.
The message tells you the problem:
runtime type tests are not allowed on some types
.The answer is to “box” the value which forces it into a reference type, and then you can type check it:
let detectTypeBoxed v = match box v with // used "box v" | :? int -> printfn "this is an int" | _ -> printfn "something else" //test detectTypeBoxed 1 detectTypeBoxed 3.14
2. Interoperability with non-F# .NET projects
The Functional Adam post What is box
in F#? nicely complements the example in the question:
The Problem - The variable was in
Int
, it was required, and if it was not provided, our application was supposed to return an error code. When the parameter was not provided, WebAPI would set the required parameter tonull
even thoughInt
cannot benull
. This was very confusing, and, to make matters worse, we were not able to make the required parameter aNullable<Int>
with WebAPI.The Solution - Box is the solution! From our searching, by boxing the
int
we could check if it isnull
even though it's not nullable (confusing). But what does the box function do? According to [the docs], thebox
function in F# "boxes a strongly typed value". Ok... Thanks documentation.That's not all very helpful, but with further digging in [the Boxing and Unboxing article of the C# Programming Guide], the answer is
Boxing is the process of converting a value type to the type
object
or to any interface type implemented by this value type. When the common language runtime (CLR) boxes a value type, it wraps the value inside aSystem.Object
instance and stores it on the managed heap. Unboxing extracts the value type from the object. Boxing is implicit; unboxing is explicit. The concept of boxing and unboxing underlies the C# unified view of the type system in which a value of any type can be treated as an object.So, boxing a value wraps the value type inside a
System.Object
and therefore we can check if the object isnull
. Then we have to unbox it back to anInt
after thenull
check.
0 comment threads