upcast
With upcast
and downcast
, we traverse the hierarchy of types. An int
is a subtype of obj. So we can move up and down between int
and obj.
In this language, we do not have the same syntax for casting that C# has. Instead, we have keywords like upcast
and downcast
for the same purpose.
Let us begin with a simple example that uses upcast
and downcast
. We create a variable with value 5. Then we cast it.
upcast
on the int
to move upwards in the type hierarchy to obj (which is the root object type).int
with the downcast
operator. It works from less derived, to more derived.let value = 5 printfn "%A" value // With upcast we move up the type hierarchy. // ... And int inherits from obj to this cast is valid. let valueObj = upcast value : obj printfn "%A" valueObj // With downcast we move down the type hierarchy. // ... We move down from obj to its derived type int. let valueInt = downcast valueObj : int printfn "%A" valueInt5 5 5
It is wonderful to be able to cast an object. But how do we know what type an obj is? We can use a match construct with a special type-testing operator.
text()
as a function that receives and object and returns a special string
message if that object is of string
type.let value = "carrot" // Cast the string to an obj. let valueObj = upcast value : obj // Match the type of the obj. // ... See if it is a string or not. let text (v : obj) = match v with | :? string -> "Is a string" | _ -> "Not a string" // Call text function with object. printfn "%A" (text valueObj)"Is a string"
Be careful with the syntax when casting in F#. If you omit a type or omit the ":" part of a downcast
or upcast
statement, a scary problem will appear.
Error FS0013: The static coercion from type 'a to 'b involves an indeterminate type based on information prior to this program point. Static coercions are not allowed on some types. Further type annotations are needed.
Types, and casting types to other types, is a complex process. With upcast
and downcast
and type pattern-matching operators, we traverse the type tree.