Open xperiandri opened 6 months ago
Hey @xperiandri 👋! This looks interesting, but can you please update the example code such that it doesn't have unresolved types? I.e., PartitionKey
and ItemRequestOptions
are not defined. Please consider using a "small reproducible example".
And can you explain what is currently not working for you? If I copy your code and fix the missing types, it just compiles. Can you update the original post such that it shows what part with inref
you are looking for?
Some related suggestions (but not the same):
Estimated cost (XS, S, M, L, XL, XXL): XS
😆 no, definitely not an XS task. XS tasks are to fix some invalid xmldocs, or add one function to, let's say, List
module. This one is definitely not XS.
And can you explain what is currently not working for you?
There are currently no conversion between 'a
and inref<'a>
, so you can't do something like the following
let foo (a: inref<string>) =
a = "foo"
foo "bar"
So, the issues is not really about CEs, but rather about supporting implicit conversion from 'a
to byref<a, ByRefKind.In>
. We already do for refs:
let foo (a: inref<string>) =
a = "foo"
let mutable f = "bar"
foo &f
Not sure we want to do it for arbitrary expressions. It's up to @dsyme.
In my mind, it is only about CEs. I have no opinion about the need for implicit conversion in other places. I'm about efficient object builders using computation expressions.
It's an interesting suggestion to allow this in a limited place (or perhaps in arguments with an opt-in attribute).
IIRC it's not hard to implement this - it was more a policy decision about code correctness and understanding semantics.
I think I'd be ok with an opt-in F#-specific attribute on the parameter as a general mechanism for call-by-address-to-inref. WDYT?
I like that idea
I'm about efficient object builders using computation expressions.
Passing it by ref won't always mean "more efficient". Passing by value oftentimes is more efficient, since JIT knows that structure is 100% on the stack and can optimize a bunch of stuff. On the other hand if you have a ref parameter, JIT doesn't really know where it's pointing - can be heap (class field), and in this case it will need write barriers.
I think I'd be ok with an opt-in F#-specific attribute on the parameter as a general mechanism for call-by-address-to-inref. WDYT?
This, or warning, should be okay.
Hm, I thought inref
is only for read-only structs and pins them to heap
Hm, I thought
inref
is only for read-only structs and pins them to heap
JIT doesn't differentiate between types of byref, so it can do defensive write barriers
Is it possible to declare a read-only struct in F#?
Is it possible to declare a read-only struct in F#?
Yes @xperiandri: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/structs#readonly-structs
Hm, I thought
inref
is only for read-only structs and pins them to heap
See: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/byrefs#inref-semantics
Notably: "There is no implication that SomeStruct
is immutable by virtue of x
being an inref
."
For pinning, you can use fixed
, iirc.
What is fixed
?
What is
fixed
?
It's way of pinning local onto stack.
@xperiandri in case sharplab is ever down, copy pasted here
open System
type PartitionKey (value : string) = struct end
type ItemRequestOptions () =
member val EnableContentResponseOnWrite = true with get, set
[<Struct>]
type CreateOperation<'a> = {
Item : 'a
PartitionKey : PartitionKey voption
RequestOptions : ItemRequestOptions
}
type CreateBuilder<'a> (enableContentResponseOnWrite : bool) =
member _.Yield _ =
{
Item = Unchecked.defaultof<_>
PartitionKey = ValueNone
RequestOptions = ItemRequestOptions (EnableContentResponseOnWrite = enableContentResponseOnWrite)
}
: CreateOperation<'a>
/// Sets the item being created
[<CustomOperation "item">]
member _.Item (state : inref<CreateOperation<_>>, item) = { state with Item = item }
/// Sets the partition key
[<CustomOperation "partitionKey">]
member _.PartitionKey (state : inref<CreateOperation<_>>, partitionKey : PartitionKey) = {
state with
PartitionKey = ValueSome partitionKey
}
/// Sets the partition key
[<CustomOperation "partitionKey">]
member _.PartitionKey (state : inref<CreateOperation<_>>, partitionKey : string) = {
state with
PartitionKey = ValueSome (PartitionKey partitionKey)
}
/// Sets the request options
[<CustomOperation "requestOptions">]
member _.RequestOptions (state : inref<CreateOperation<_>>, options : ItemRequestOptions) = {
state with
RequestOptions = options
}
let create<'a> = CreateBuilder<'a>(false)
let operation = create {
item "item"
partitionKey "partitionKey"
}
()
I propose we support accepting
inref<'T>
as the first parameter of computation expression operationThe existing way of approaching this problem in F# is not using
inref
s and causing structs to be copiedPros and Cons
The advantages of making this adjustment to F# are performant object builders using computation expressions and value type state
The disadvantages of making this adjustment to F# do not exist
Extra information
Estimated cost (XS, S, M, L, XL, XXL): XS
Related suggestions: No
Affidavit
Please tick these items by placing a cross in the box:
Please tick all that apply:
For Readers
If you would like to see this issue implemented, please click the :+1: emoji on this issue. These counts are used to generally order the suggestions by engagement.