3
let iter2D (map: 'T byref -> unit) (arr: 'T[][]) =
    for y = 0 to arr.Length - 1 do
        let row = arr.[y]
        for x = 0 to row.Length - 1 do
            let mutable elem = arr.[y].[x]
            map &elem

The last line has: "The address of the variable 'elem' cannot be used at this point." What's wrong?

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
Asik
  • 21,506
  • 6
  • 72
  • 131

1 Answers1

8

In F# 'T byref appears as a regular type, but under the cover, it is not - it corresponds to ref and out parameters in C# and those are special annotations on method arguments. This is why 'T byref is a bit odd in F#.

I think you won't be able to use it through ordinary F# functions, because a function T1 -> T2 is compiled as FSharpFunc<T1, T2> with a method T2 Invoke(T1 arg) - and you cannot pass the byref type to generics (as it is not a real type).

A workaround is to define your own delegate that has byref type:

type FastAction<'T> = delegate of 'T byref -> unit

With this, you can write iter2D that iterates directly over the array:

let iter2D (map:FastAction<'T>) (arr: 'T[][]) =
    for y = 0 to arr.Length - 1 do
        let row = arr.[y]
        for x = 0 to row.Length - 1 do
            map.Invoke(&arr.[y].[x])

The following will then mutate the value inside the array:

let arr = [| [| 0 |] |]
iter2D (FastAction(fun a -> a <- 10)) arr
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • 1
    What do you mean by saying that `byref` is not real type? Then what is [`MakeByRefType()`](https://msdn.microsoft.com/library/system.type.makebyreftype.aspx) returning? And what is [`IsByRef`](https://msdn.microsoft.com/library/system.type.isbyref.aspx) property for? – user4003407 Apr 11 '16 at 03:44
  • Yuk, `FastAction(fun bla ...)` causes 2 allocations... Seems like there's no way around it though, F# doesn't have a separate anonymous delegate syntax like C#. – Asik Apr 11 '16 at 03:48
  • @Asik they do since F# 4 or 5, there’s now auto conversion into delegates. However, this still won’t work with byref, as they cannot be captured by closures (obviously), and the compiler won’t detect you’re only using your lambda as a delegate. – Abel Jul 28 '22 at 10:05