How to work with byref and pipe operator?

    let testByref (x: int byref) s =
        x <- x + 1
        printfn "%s %d" s x

    let mutable x = 0

    // Not working. **How to make this kind of usage valid?**
    "hi" |> testByref &x

    // Working
    testByref &x "hi"

Your code works to me:

hi 1
hi 2

Fiddle

So, what do you mean by “not working”?


It reports above error.

See this issue on GitHub. This is intended.

But that’s not exactly the same issue, right?

Anyway, curious, why it was allowed before? I’d expect at least mention about such changes.

This is exactly the same issue.

There were massive, severe, horribly broken holes in byref programming in F#. For example, you could do bizarre things like allow byrefs to escape the scopes in which they were defined, which might accidentally work, but in practice would likely just lead to crashing code.

Changes in byref scoping and allowed use were mentioned in the F# 4.5 blog post, RFC, and soon docs will be updated (there used to not even be a topic on byref programming and behavior with it.).

2 Likes

The reason I asked about this is because when I am trying to use GitHub project EFCore.FSharp I found the cloned code base is not compiled and some lines are using this pattern. Please check: https://github.com/bricelam/EFCore.FSharp/blob/8f05755efa6b5e90d866ef995cfb9601b7e23991/EFCore.FSharp/Migrations/Design/FSharpSnapshotGenerator.fs#L172

By the way, if some expert can contribute to this project it would be fantastic because EF Core is so great and used in a lot of places but without full support by F# which is so sad for me. And I don`t know how to make this project work for me so far.

As an aside, this kind of code was disallowed in F# 4.1 as well, which dates back to March 2017 for the release. F# 4.0 used to allow this. In F# 4.5, we have far more intricate checks in place that affect other areas, but the end result is the same as if you’d used F# 4.1 or F# 4.5.

.NET Fiddle is still on F# 4.0 it seems.

Building the project, it looks like there are 14 places where there is this incorrect usage of byref.

Looking a bit closer, I wonder if they’re mistakenly using byref when they actually mean ref. The two are fundamentally different concepts. byref is a pointer. ref is a storage location for a value that was (a) the primary story for mutability in F# for a long time, and (b) the way you pass things by reference in F#.

On second thought, byref is probably still the best way to pass something by reference, since it won’t incur the extra allocations and will still mostly have the same semantics.

However, application of a byref to a generic function that causes its type to be specialized is unsupported as of F# 4.1, and even more unsupported as of F# 4.5.