One can use PrimVar Int from primitive, which is essentially a ByteArray# fitting one Int. That representation however has additional heap object and need to follow a pointer on access. MutInt# would be mutable bits directly inside the object.
More generally, we could want to have a primitive for unboxed representations, say Ref#, generalising MutInt# and possible MutDouble#, MutWord# etc; but I think MutInt# is easier to implement, and enough for my current needs.
The more general Ref# could also have specialised versions like
casRefInt#::Ref#dInt#->Int#->Int#->State#d->(#State#d,Int##)casRefInt8#::Ref#dInt8#->Int8#->Int8#->State#d->(#State#d,Int8##)...-- like `casIntArray#` etc friends
but again, I think MutInt# is hopefully easier to implement; and if Ref# is later done too, one could change type MutInt# = Ref# Int#.
Edited
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Child items
...
Show closed items
Linked items
0
Link issues together to show that they're related or that one is blocking others.
Learn more.
@Bodigrim which has zero comments on what it is, how and why it works (and when it doesn't).
As far as I understand, the Counter is an object itself, so about the same as unpacked ByteArray#, the data is still a pointer away; i.e. cannot unpacked into the parent type.
MutInt# should not be an UnliftedType, it should be bytes inside the data. IntRep if that's ok to be mutable.
I want to have a mutable int field inside the object having memory layout like class Foo { STG_Header foo; int m_one; int m_two; }. I don't know whether Counter achieves that, it doesn't look like so.
EDIT: now if I need multiple mutable values, the best option (memory layout wise) seems to be to use a single ByteArray# for all of them, at the cost of quite a lot of boilerplate.
I want to have a mutable int field inside the object having memory layout like class Foo { STG_Header foo; int m_one; int m_two; }. I don't know whether Counter achieves that, it doesn't look like so.
It sounds like you are essentially asking for mutable (unboxed/unlifted) fields along the lines of the multable fields proposal.
Run into this "at work", i.e. in real industrial setting. Having IORef Int, or PrimVar s Int (i.e. MutableByteArray# in disguise) just feels bad. Boxes for no benefit.
I've been thinking about this for a while too. I agree that it would be nice to have. The design space is quite large though. Some thoughts:
Retrofitting mutable fields on datacons seems difficult because datacons don't have an identity. GHC is free to unpack and repack datacons at will (cf reallyUnsafePtrEquality). A mutable fields would basically be an offset in a datacon object so GHC would not be allowed to mess with datacons containing mutable fields... This would be difficult to ensure.
As such, I believe that a better premise is to start with a new object type that is representation polymorphic. Say Struct# s a.
Approach 1:
A Struct# s a is represented internally like a data Foo = Foo a. But Struct# s a has an identity (you can't pattern match on it; GHC doesn't unpack it; etc.), just like [Mutable]ByteArray, Small[Mutable]Array...
We can also add a dirty bit to track mutation of the object and a lock field to perform atomic updates.
A Struct# s a is unlifted: we can directly access its fields without worrying about bottom.
a can be any type. In particular it can be an unboxed tuple. This allows mixing unboxed values (like the Int# we want), with other lifted fields.
We can build a Struct# s a from a a, read it, write it, and update it atomically:
primtypeStruct#sa-- a is any type (even unboxed tuples/sums)newStruct#::a->State#s->(#State#s,Struct#sa#)readStruct#::Struct#sa->State#s->(#State#s,a#)writeStruct#::Struct#sa->a->State#s->State#satomicUpdateStruct#::(a->a)->Struct#sa->State#s->State#s
Approach 2a:
If we want to be able to read/update a single field (e.g. to avoid locking the whole object when doing atomic updates), we need to be more creative. We could maybe use a list of types instead of a and use a Nat index to read/write the fields:
primtypeStruct#sa-- a is a list of types now, e.g. `foo :: Struct# s [Int#, Int#, Bool]`newStruct#::Tuple#a->State#s->(#State#s,Struct#sa#)readStruct#::Struct#sa->State#s->(#State#s,Tuple#a#)writeStruct#::Struct#sa->Tuple#a->State#s->State#sreadStructField#::forall(n::Nat)->Struct#sa->State#s->(#State#s,Indexna#)writeStructField#::forall(n::Nat)->Indexna->Struct#sa->State#s->State#scasStructField#::forall(n::Nat)->Indexna->Indexna->Struct#sa->State#s->(#State#s,Indexna#)
Approach 2b:
Alternatively we could support pattern matching on Struct# s a but instead of getting the value of each field, we would get an accessor to each field:
primtypeStructField#sa-- internally: an unboxed 2-tuple containing a pointer to a Struct# and a "field selector" (an offset for the native rts, something else for the JS rts)readStructField#::StructField#sa->State#s->(#State#s,a#)writeStructField#::StructField#sa->a->State#s->State#scasStructField#::StructField#sa->a->a->State#s->(#State#s,a#)casemystruct::Structs[Int#,Int#,Bool]of(#field1Int::StructField#sInt#,field2Int,field3Bool#)->casewriteStructField#field2Int17#sof...