Why must a struct value be mutable to set an indexed property? - f#

Consider the following program:
[<Struct>]
type Grid2D<'T> =
val RowLength : int
val Data : 'T[]
new(rowLength, data) = { RowLength = rowLength; Data = data }
member this.Item
with get(rowIndex, columnIndex) =
this.Data.[rowIndex * this.RowLength + columnIndex]
and set(rowIndex, columnIndex) value =
this.Data.[rowIndex * this.RowLength + columnIndex] <- value
let g = Grid2D(3, Array.zeroCreate(3 * 3))
g.[1, 1] <- 4
The last line fails to compile with:
error FS0256: A value must be mutable in order to mutate the contents
or take the address of a value type, e.g. 'let mutable x = ...'
However, if the [<Struct>] attribute is removed, and Grid2D is thus a reference type, then the program compiles.
Interestingly, inlining the property setter by hand also compiles fine:
g.Data.[1 * g.RowLength + 1] <- 4
So why is calling it a compile error?
Note: I am aware that this compiler error exists to make it impossible to mutate a non-mutable value of a struct by setting one of its fields. But I'm clearly not mutating the struct here.

I'm gonna take a guess here that its the second part of that error message that applies - "or take the address of a value type". Its not the mutability but the address of the value type that needs to be taken in order for you to refer to the same value g when mutating the Data.

It's probably impossible the compiler could consistently prove any setter doesn't actually mutate the struct, so it doesn't bother and just always emits the error when using assignment statements on non-mutable struct bindings.
In other words the question becomes: why does F# assume property setters mutate their instance? Well, probably because that's usually what property setters do.
Inlining the property setter works in this case because then the target of the assignment is an element of a property and not a property of the struct itself.

Related

IgnoreMissingMember setting doesn't seem to work with FSharpLu.Json deserializer

This is a following to: deserialization issue, with json.net, in F#.
I am deserializing some JSON that has an extra, unbound property using FSharpLu.Json. Here is the code:
open System
open Newtonsoft.Json
open Microsoft.FSharpLu.Json
type r =
{
a: int
}
let a =
"{\"a\":3, \"b\":5}"
Compact.TupleAsArraySettings.settings.MissingMemberHandling <- MissingMemberHandling.Ignore
Compact.deserialize<r> a // doesn't work
Despite setting MissingMemberHandling.Ignore it returns a json.net error:
Could not find member 'b' on object of type 'r'. Path 'b', line 1, position 13.
Is there a way to make this work, or is it an issue with FSharpLu.Json?
Here is the fiddle: https://dotnetfiddle.net/OsVv1M
as a side note, there is another deserializer in FSharpLu.Json and I can get that code to work with it:
FSharpLu.Json.Default.Internal.DefaultSettings.settings.MissingMemberHandling <- MissingMemberHandling.Ignore
Default.deserialize<r> a
will work, but that deserializer doesn't handle discriminated unions... so I need to get the compact one to work.
When looking into the source of FSharpLu.Json, I found this:
/// Compact serialization where tuples are serialized as heterogeneous arrays
type TupleAsArraySettings =
static member formatting = Formatting.Indented
static member settings =
JsonSerializerSettings(
NullValueHandling = NullValueHandling.Ignore,
// MissingMemberHandling is not technically needed for
// compact serialization but it avoids certain ambiguities
// that guarantee that deserialization coincides with the
// default Json.Net deserialization.
// (where 'coincides' means 'if the deserialization succeeds they both return the same object')
// This allows us to easily define the BackwardCompatible
// serializer (that handles both Compact and Default Json format) by reusing
// the Compact deserializer.
MissingMemberHandling = MissingMemberHandling.Error,
Converters = [| CompactUnionJsonConverter(true, true) |]
)
so they are explicitly setting the MissingMemberHandling to Error; maybe the solution is to instantiate the deserializer, change the setting and then use it.
The serializer setting you are trying to mutate, Compact.TupleAsArraySettings.settings, is a static member, as is shown in the code:
type TupleAsArraySettings =
static member formatting = Formatting.Indented
static member settings =
JsonSerializerSettings(
NullValueHandling = NullValueHandling.Ignore,
// MissingMemberHandling is not technically needed for
// compact serialization but it avoids certain ambiguities
// that guarantee that deserialization coincides with the
// default Json.Net deserialization.
// (where 'coincides' means 'if the deserialization succeeds they both return the same object')
// This allows us to easily define the BackwardCompatible
// serializer (that handles both Compact and Default Json format) by reusing
// the Compact deserializer.
MissingMemberHandling = MissingMemberHandling.Error,
Converters = [| CompactUnionJsonConverter(true, true) |]
)
Since a member is actually a member function (i.e. a method) as explained F# for fun and profit: Attaching functions to types, settings is actually (in c# terminology) a static property returning a new instance of JsonSerializerSettings each time it is invoked. To test this, we can do:
printfn "%b" (Object.ReferenceEquals(Compact.TupleAsArraySettings.settings, Compact.TupleAsArraySettings.settings)) // prints "false"
Which prints "false". Thus mutating the returned value has no effect on the behavior of Compact. A static field in the c# sense would be defined by a static let statement; if settings returned such a field, mutating its contents would have had an effect.
In any event modifying the value of Compact.TupleAsArraySettings.settings.MissingMemberHandling seems unwise as doing so would modify the behavior of of Compact.deserialize throughout your entire AppDomain in a way that might break backwards compatibility with Json.NET native serialization. As explained in the code comments above, the setting is required to make BackwardCompatible.deserialize work. But why might that be? Since Json.NET's native format for option and discriminated unions looks like:
{
"a": {
"Case": "Some",
"Fields": [
3
]
}
}
We can guess that MissingMemberHandling is used to trap situations where "Case" and "Fields" are found or not, and switch from one algorithm to the other.
If you are certain you do not need to deserialize f# types in Json.NET format, it seems you should be able to use CompactUnionJsonConverter directly as follows:
let settings = JsonSerializerSettings(
NullValueHandling = NullValueHandling.Ignore,
Converters = [| CompactUnionJsonConverter(true, true) |]
)
let c = JsonConvert.DeserializeObject<r>(a, settings)
let json2 = JsonConvert.SerializeObject(c, Formatting.Indented, settings)
Demo fiddle here.

Explanation and idiomatic solution(s) for assignments to arrays in classes in F#

I've been trying out F# a bit recently, but I've almost exclusively used records and immutability. Now I'm trying mutable classes, however, and it's giving me some issues.
The biggest one so far is that it seems I can't assign to an element of an array that is a member of a class. For example, the following works as expected, replacing the 0th element with a 9:
let array = [| 0..5 |]
array.[0] <- 9
printfn "%A" array
However, this does not work as I would expect:
type MyClass () =
member this.Array = [| 0..5 |]
let myClass = MyClass()
myClass.Array.[0] <- 9
printfn "%A" myClass.Array
The assignment seems to do nothing, but the compiler does not complain with even a warning. I assume there is something that prevents assignments to members of classes, including their submembers and elements, but I can't find any documentation or outside explanations of it.
Could someone help me understand what's going on here and give an example or two of how this is typically done?
MyClass.Array, the way you declared it, is a readonly property. You gave it a get accessor, and the body of that accessor consists of creating an array and returning it.
Spot the problem yet? It's a new array every time you call MyClass.Array. The first array does get mutated just fine, but then you just throw it away and request a new array from the class.
If you want to "store" an array "inside" the class, you need to give it a name, and then make your property accessor refer to that name. That way, the property accessor will return the same array every time:
type MyClass() =
let theArray = [|0..5|]
member this.Array = theArray
(incidentally, this very gotcha is one of the reasons to avoid mutable data)
This is probably because the declaration you have defines this.Array as a read-only property. As mentioned in another answer, the code you have returns a new array for each call.
An auto property works and is a little more concise:
type MyClass () =
member val Array = [| 0..5 |] with get,set
let myClass = MyClass()
myClass.Array.[0] <- 9
printfn "%A" myClass.Array // [|9; 1; 2; 3; 4; 5|]

F# cons operator and list in class

So here is my question. I have a python project that is very functionally oriented...but which fundamentally runs with classes...so F# seemed the ideal language for a compiled version of my code (I'd like to black box my source code...the python code is just sitting there in files. Not cool).
In my python class, I have a generic container--a python list. This container will contain arrays of a uniform length in a given implementation...
Here is my problem: I need to add arrays to the list after initialization.
What is the proper way to add arrays to the list in the class?
Here is what I have...but it seems like it will be slow, since it throws out the old list with a new copy (right??):
type myclass() =
let mutable _training = []
member this.training with get() = _training
and set(value) = _training <- value::_training
However, this fails (even after the edits suggested below) because the compiler claims that the set function is an obj with set rather than an obj list with set, even though it acknowledges that _training is a mutable obj list on the left side of the <- in the set function...I'm stumped)
What is the proper "F#" way to do this?
The 'proper' F# way to do it is to not have that myclass class at all. A class with a single getter and setter provides no value over an immutable list.
That said, the above code doesn't compile for a number of reasons:
A type is defined with =, not :.
_training isn't bound by with a let expression.
If you want to mutate the _training value, you must declare it let mutable
Even so, the return value of get is obj list, but the input value of set (which is the symbol value) is obj, because of the use of the cons operator (::). As the compiler says, the type of get and set must be the same.
You can make it compile by addressing all these concerns:
type Myclass() =
let mutable _training = []
member this.training with get() = _training
and set(value) = _training <- value
but it would accomplish nothing. If you need a list, then pass a list. It's not that we don't believe in encapsulation in F#, but properties aren't equivalent to encapsulation anyway...
BTW, it'd be idiomatic to name types using Pascal Case, so MyClass instead of myclass. The leading underscore isn't used much either, so it should be training instead of _training.
The problem is that a property has a type,
and the getter and setter must be compatible with that type.
If I run your code interactively, the compiler helpfully tells me exactly that!
type myclass() =
let mutable _training = []
member this.training
with get() = _training
and set(value) = _training <- value::_training
// error FS3172: A property's getter and setter must have the same type.
// Property 'training' has getter of type 'obj list'
// but setter of type 'obj'.
The answer is simply to have a different method that prepends an element
to the list:
type myclass() =
let mutable _training = []
member this.training
with get() = _training
and set(newTraining) = _training <- newTraining
member this.prepend(element) =
_training <- (element::_training)
member this.prependList(list) =
_training <- (list # _training)
// test
let myList = myclass()
myList.prepend(1)
myList.prepend(2)
myList.training // output => [2; 1]
myList.prependList([3;4])
myList.training // output => [3; 4; 2; 1]
It's not a nice implementation from a functional point of view,
but it does answer your question.
Note that if you do want a pure functional implementation,
you don't need a class at all.
// test
let myList = []
let myList2 = 1::myList
let myList3 = 2::myList2
// myList3 => [2; 1]
let myList4 = [3;4] # myList3
// myList4 => [3; 4; 2; 1]
Now, how to work with immutable state in a nice way -- that's a whole
other question! :)

How to access UnsafeMutablePointer struct member

let cfg: UnsafeMutablePointer <pjsua_config> = nil;
pjsua_config_default(cfg);
cfg.cb // Error: Value of type 'UnsafeMutablePointer<pjsua_config>' has no member 'cb'
How to cast cfg to access it's fields ? I've tried to find answer in Apple's 'Interacting with C API' document
but there is no info related to this issue.
Actually there is related information in the "Interacting with C APIs"
chapter:
When a function is declared as taking an UnsafeMutablePointer<Type> argument, it can accept any of the following:
...
An inout expression whose operand is a stored lvalue of type Type, which is passed as the address of the lvalue
...
So you can simply pass the address of an (initialized) pjsua_config structure as "inout-parameter" with &, similar as you would do in C:
var cfg = pjsua_config() // creates a `struct pjsua_config` with all fields set to zero
pjsua_config_default(&cfg)
Now you can access cfg.cb, and you don't have to worry about
memory management.
I don't know what you are doing in
pjsua_config_default() method but to get raw data you shud call memory on your pointer.
Please see working example:
var pt : UnsafeMutablePointer<Int>? // Optional
pt = UnsafeMutablePointer.alloc(1)
pt!.initialize(22)
pt!.memory // returns your value
// Cleanup
pt!.destroy()
pt!.dealloc(1)
pt! = nil

Swift: Getting immutable error with mutable Array [duplicate]

Why is it that I cannot mutate an implicitly unwrapped optional variable?
Here is a short example the reproduces the issue:
With Array
var list: [Int]! = [1]
list.append(10) // Error here
Immutable value of type '[Int]' only has mutating members named 'append'
With Int
var number: Int! = 1
number = 2
number = 2 + number
number += 2 // Error here
Could not find an overload for '+=' that accepts the supplied arguments
Because the way you are trying to mutate them is by mutating the values (which are immutable) instead of mutating the var.
In Swift value types are immutable. All and always.
Mutation is not a mutation of the value, it's a mutation of the variable that contains the value.
In the case of the Int, the += operator gets a structure on the left and an Int on the right, and it cannot add a structure to an int.
In the case of the Array the append is a mutating member. But it's being invoked on an immutable value that is not directly stored in a variable. It can only operate on values that are directly stored in a variable (which is what makes them mutable: the fact that they are stored in a variable. They are not really mutable, the variable is).
Update:
This has been fixed in Xcode Beta 5 with one small caveat:
var list: [Int]! = [1]
list.append(10)
var number: Int! = 1
number! += 2
number += 2 // compile error
The array works as expected, but it seems that right now the integer still requires an explicit unwrap to allow using +=
Currently, this is just the nature of Optionals (whether implicitly unwrapped or not). The unwrap operator returns an immutable value. This is likely to be fixed or a better solution will be provided in the future.
The only way around it for now is to wrap the array in a class:
class IntArray {
var elements : [Int]
}

Resources