Given a the .Net Type, say typeof<string>
, at runtime how does one created the equivalent of string list = []
?
My motivation is that when using FSharpValue.MakeRecord
to create a record based on values parsed the values need to be passed as a obj[]
. I've been up casting the arguments using box
and that has worked except for with lists. The issue I've encountered is that a empty untyped list list can't be boxed and then unboxed. The specific error returned is:
System.InvalidCastException: Unable to cast object of type
'Microsoft.FSharp.Collections.FSharpList`1[System.Object]'
to type
'Microsoft.FSharp.Collections.FSharpList`1[System.String]'.
An empty typed list can be boxed and unboxed so I've tried to find a way to cast a list to a runtime type, e.g. a Type returned by typeof<> but with no luck.
type Test = {Names : string list}
// fails
let genericList = []
{Names = unbox (box genericList)}
//works
let typedList : string list = []
{Names = unbox (box typedList)}
//works
let genericNonEmptyList = ["Bill"]
{Names = unbox (box genericNonEmptyList)}
You can create an empty list using an empty pair of square brackets [] or the type constructor list() , a built-in function that creates an empty list when no arguments are passed. Square brackets [] are commonly used in Python to create empty lists because it is faster and more concise.
To create an empty list, use [] for a growable list or List. empty for a fixed length list (or where growability is determined at run-time). The created list is fixed-length if length is provided. The list has length 0 and is growable if length is omitted.
Using reflection you can get the List
module and call the generic empty
method:
open System
open System.Reflection
let emptyList (t:Type) =
Assembly.GetAssembly(typeof<_ list>)
.GetType("Microsoft.FSharp.Collections.ListModule")
.GetMethod("Empty", BindingFlags.Static ||| BindingFlags.Public)
.MakeGenericMethod(t)
.Invoke(null, [||])
use as follows:
let itemType = typeof<string>
let emptyStringList = emptyList(itemType) :?> string list
If you are calling that quite often, consider caching (reduces execution time by ~1/3):
let emptyList =
let empty =
Assembly.GetAssembly(typeof<_ list>)
.GetType("Microsoft.FSharp.Collections.ListModule")
.GetMethod("Empty", BindingFlags.Static ||| BindingFlags.Public)
fun (t:Type) -> empty.MakeGenericMethod(t).Invoke(null, [||])
Let me add one more alternative answer - although both of the existing methods work, they rely on understanding how F# represents lists. In the first case, you need to know there is Empty
method and in the second case, you need to know there is a union case called Empty
.
I generally prefer to do this by defining a helper type and using reflection over my custom type:
type ListHelper =
static member Empty<'T>() : list<'T> = []
let makeEmpty =
let empty = typeof<ListHelper>.GetMethod("Empty")
let emptyArr : obj[] = [| |]
fun ty -> empty.MakeGenericMethod([| ty |]).Invoke(null, emptyArr)
This gives you quite simple function that can cache the MethodInfo
(you could even use Expression
to pre-compile and cache the invocations) and does not rely on clever tricks.
@CaringDev's answer using .NET reflection is fine, but you can also use the F#-specific reflection module to create instances of union cases:
let empty ty =
let uc =
Reflection.FSharpType.GetUnionCases(typedefof<_ list>.MakeGenericType [|ty|])
|> Seq.filter (fun uc -> uc.Name = "Empty")
|> Seq.exactlyOne
Reflection.FSharpValue.MakeUnion(uc, [||])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With