I am trying to achieve the automatic/dynamic cast in the fourth line below:
let a = 1 // type: int
let b = box a // type: obj
b.GetType() // System.Int32, so it is perfectly aware what it is!
let c = unbox b // fails....
The following would would work in the final line above BUT would require me to know and explicitly mark ahead-of-time the primitive/value type that I am working with (which I am trying to avoid):
let c1:int = unbox b
let c2 = b :?> int
While b knows at run-time what it is, the compiler doesn't, because it's an obj.
If you know, at compile time, what it is, you can unbox it like this:
let a = 1
let b = box a
b.GetType()
let c = unbox<int> b
c is now an int.
unbox only does anything if the type can be explicitly or implicitly determined at compile time. Here it will implicitly (and wrongly) try to convert the object to a string, as that's how it is used in the subsequent line.
let a = 1
let b = box a
b.GetType()
let c = unbox b
printf "%s" c
This of course gives a runtime error because it is not a string.
There's no way to have unbox convert to "what it actually is under the hood", as there's no definite way of determining this at compile time. There may be another way to do what you're trying to do though, if you can provide more details.
If you're wanting to, say create a generic unboxed list from boxed objects, you can do something like this:
let addToList (l: 'a list) (o: obj) = // type annotations optional
let o' = unbox o // unboxes to generic type 'a
o'::l
let l = [1;2;3]
let b = box 4
let l' = addToList l b // l' is list<int>, not list<obj>
let l2 = [1.;2.;3.]
let b2 = box 4.
let l2' = addToList l2 b2 // l2' is list<float>
// but as above you still have to be careful
let lcrash = addToList l b2 // crash
Related
I would like to extend F# Arrays such that I can use arrays without converting to the finite int. Instead I want to work with bigint directly.
I was able to add a second length method to the array type as follows:
type 'T ``[]`` with
member this.LengthI: bigint =
bigint this.Length
member this.Item(index: bigint): 'T =
this.[int index]
However the Item method cannot be called with the .[ ] syntax.
Any ideas how this could be achieved? I this possible at all?
I strongly suspect this isn't possible for native arrays. You can verify yourself that you can overload indexed access just fine for other collections.
If you compile the following code:
let myArray = [| "a" |]
let myList = [ "a" ]
let arrayElement = myArray.[11111]
let listElement = myList.[22222]
and inspect the resulting IL, you'll see that while accessing the list element compiles to a regular virtual call, there is a special CIL instruction for accessing a native array element, ldelem.
//000004: let arrayElement = myArray.[11111]
IL_002c: call string[] Fuduoqv1565::get_myArray()
IL_0031: ldc.i4 0x2b67
IL_0036: ldelem [mscorlib]System.String
IL_003b: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::arrayElement#4
.line 5,5 : 1,33 ''
//000005: let listElement = myList.[22222]
IL_0040: call class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string> Fuduoqv1565::get_myList()
IL_0045: ldc.i4 0x56ce
IL_004a: callvirt instance !0 class [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1<string>::get_Item(int32)
IL_004f: stsfld string '<StartupCode$51dff40d-e00b-40e4-b9cc-15309089d437>'.$Fuduoqv1565::listElement#5
IL_0054: ret
I would guess that the same compiler logic that special-case array access to that single instruction also bypass any overload resolution involving extension methods and the like.
One way to circumvent this is to wrap the array in a custom type, where overloaded indexers will work as you expect. Making the wrapper type a struct should reduce the performance loss in most cases:
type [<Struct>] BigArray<'T>(array : 'T[]) =
member this.LengthI: bigint =
bigint array.Length
member this.Item
with get(index : int) = array.[index]
and set (index : int) value = array.[index] <- value
member this.Item
with get(index : bigint) = array.[int index]
and set (index : bigint) value = array.[int index] <- value
let bigArray = BigArray myArray
let bigArrayElement = bigArray.[0]
let bigArrayElement2 = bigArray.[bigint 0]
Another one is to upcast the array to the base System.Array class, on which you can then define the same overloaded operator. This removes the need to create a wrapper type and duplicate all members of 'T[], as you can just upcast/downcast the same array object as necessary. However, since the base class is untyped, you will lose type safety and have to box/unbox the elements when using the indexed access, which is quite ugly:
type System.Array with
member this.Item
with get (index : int) = (this :?> 'T[]).[index]
and set (index : int) (value : 'T) = (this :?> 'T[]).[index] <- value
member this.Item
with get(index : bigint) : 'T = (this :?> 'T[]).[int index]
and set(index : bigint) (value : 'T) = (this :?> 'T[]).[int index] <- value
let untypedArray = myArray :> System.Array
let untypedArrayElement = box untypedArray.[0] :?> string
let untypedArrayElement2 = box untypedArray.[bigint 0] :?> string
type bytesLookup = Map<byte,int list>
type lookupList = bytesLookup list
let maps:bytesLookup = Map.empty
let printArg arg = printfn(Printf.TextWriterFormat<unit>(arg))
let array1 = [|byte(0x02);byte(0xB1);byte(0xA3);byte(0x02);byte(0x18);byte(0x2F)|]
let InitializeNew(maps:bytesLookup,element,index) =
maps.Add(element,List.empty<int>)(*KeyNotFoundException*)
maps.[element]
let MapArray (arr:byte[],maps:bytesLookup ) =
for i in 0..arr.Length do
match maps.TryFind(arr.[i]) with
| Some(e) -> i::e
| None -> InitializeNew(maps,arr.[i],i)
MapArray(array1,maps);
printArg( maps.Count.ToString())
Exception
System.Collections.Generic.KeyNotFoundException: The given key was not
present in the dictionary. at
Microsoft.FSharp.Collections.MapTreeModule.find[TValue,a](IComparer1
comparer, TValue k, MapTree2 m) at
Microsoft.FSharp.Collections.FSharpMap2.get_Item(TKey key) at
FSI_0012.MapArray(Byte[] arr, FSharpMap2 maps) in Script1.fsx:line 16
at .$FSI_0012.main#() in Script1.fsx:line 20
In the function I'm trying to initialize a new element in the map with a list of int. I also try to push a new int value into the list at the same time.
What am I doing wrong?
F# Map is an immutable data structure, the Add method doesn't modify the existing data structure, it returns a new Map with the additions you've requested.
Observe:
let ex1 =
let maps = Map.empty<byte, int list>
maps.Add(1uy, [1]) // compiler warning here!
maps.[1uy]
Two things about this code:
It throws System.Collections.Generic.KeyNotFoundException when you run it
It gives you a compiler warning that the line maps.Add... should have type unit but actually has type Map<byte,int list>. Don't ignore the warning!
Now try this:
let ex2 =
let maps = Map.empty<byte, int list>
let maps2 = maps.Add(1uy, [1])
maps2.[1uy]
No warning. No exception. Code works as expected, returning the value [1].
Say I have a code like this
let a = new List<int>()
let b = a :> obj :?> List<obj>
It throws an exception saying that it can't do it since one is List<int> while I'm trying to make it an List<obj>.
I understand why that's a problem. It can't just magically create an interface for me that replaces all int types with obj, but what CAN I do here?
I have an object and I know that's it's a List of something. How can I access the elements and just not care about their type?
My concrete example doesn't use Lists so I require a general, not a List specific, solution.
In case of lists you can use System.Collections.IList to access elements
open System.Collections
open System.Collections.Generic
let x = List<int>()
let y: IList = downcast (x :> obj)
This approach can also be generalized: make your generic classes implement non-generic interface:
type IT =
abstract Value: obj
type T<'a>(a: 'a) =
member val Value = a;
interface IT with
member this.Value = upcast this.Value
If this is not an option (i.e. because you cannot make changes in classes) you can always resort to reflection
type T<'a>(a: 'a) =
member val Value = a;
type Action =
static member Do(a: T<_>) = printfn "%A" a.Value
let v = T(10)
let mi = typeof<Action>.GetMethod("Do").MakeGenericMethod(v.GetType().GetGenericArguments().[0])
mi.Invoke(null, [|v|])
I have an array and I want to cast it to an object. This is my code.
let a = [| 1 |]
let b = a :?> obj
but it tells me that int [] has no proper subtypes and cannot be used as a source of runtime type coercion. I'm pretty sure I can always do (object) in C# without the compiler complaining, so what's the issue here?
You're trying to downcast (:?>), instead of upcast (:>). Your code should be:
let a = [| 1 |]
let b = a :> obj
Please see http://msdn.microsoft.com/en-us/library/dd233220.aspx for more details.
While ebb's answer is of course correct, there is another solution for the special case of upcasting to obj, which is the box operator:
let a = [| 1 |]
let b = box a
In F#, box doesn't have the same meaning as it does in the CLR, where it means "make a reference-type object corresponding to a value-type value." In F#, it just means "cast to obj." Because of this, you can use box with reference types as well as with value types.
Why is disabled types like
type t = A of int | B of string * mutable int
while such types are allowed:
type t = A of int | B of string * int ref
The question is, how would you modify the value of a mutable element of discriminated union case? For ref types, this is quite easy, because ref is a reference cell (a record actually) which contains the mutable value:
match tval with
| B(str, refNum) -> refNum := 4
We extract the reference cell and assign it to a new symbol (or a new variable) refNum. Then we modify the value inside the ref cell, which also modifies tval, because the two references to the cell (from discriminated union case and from refNum variable) are aliased.
On the other hand, when you write let mutable n = 0, you're creating a variable, which can be directly mutated, but there is no cell holding the mutable value - the variable n is directly mutable. This shows the difference:
let mutable a = 10
let mutable b = a
b <- 5 // a = 10, b = 5
let a = ref 10
let b = a
b := 5 // a = 5, b = 5 (because of aliasing!)
So, to answer your question - there is no way to directly refer to the value stored inside the discriminated union case. You can only extract it using pattern matching, but that copies the value to a new variable. This means that there isn't any way you could modify the mutable value.
EDIT
To demonstrate limitations of mutable values in F#, here is one more example - you cannot capture mutable values inside a closure:
let foo() =
let mutable n = 0
(fun () -> n <- n + 1; n) // error FS0407
I think the reason is same as with discriminated union cases (even though it's not as obvious in this case). The compiler needs to copy the variable - it is stored as a local variable and as a field in the generated closure. And when copying, you want to modify the same variable from multiple references, so aliasing semantics is the only reasonable thing to do...
Ref is a type (int ref = ref<int>). Mutable is not a type, it's a keyword that allows you to update a value.
Example:
let (bla:ref<int>) = ref 0 //yup
let (bla:mutable<int>) = 3 //no!