[#105544] [Ruby master Feature#18239] Variable Width Allocation: Strings — "peterzhu2118 (Peter Zhu)" <noreply@...>

Issue #18239 has been reported by peterzhu2118 (Peter Zhu).

18 messages 2021/10/04

[#105566] [Ruby master Bug#18242] Parser makes multiple assignment sad in confusing way — "danh337 (Dan Higgins)" <noreply@...>

Issue #18242 has been reported by danh337 (Dan Higgins).

9 messages 2021/10/06

[#105573] [Ruby master Bug#18243] Ractor.make_shareable does not freeze the receiver of a Proc but allows accessing ivars of it — "Eregon (Benoit Daloze)" <noreply@...>

Issue #18243 has been reported by Eregon (Benoit Daloze).

11 messages 2021/10/06

[#105618] [Ruby master Bug#18249] The ABI version of dev builds of CRuby does not correspond to the ABI — "Eregon (Benoit Daloze)" <noreply@...>

Issue #18249 has been reported by Eregon (Benoit Daloze).

23 messages 2021/10/11

[#105626] [Ruby master Bug#18250] Anonymous variables seem to break `Ractor.make_shareable` — "tenderlovemaking (Aaron Patterson)" <noreply@...>

Issue #18250 has been reported by tenderlovemaking (Aaron Patterson).

14 messages 2021/10/12

[#105660] [Ruby master Feature#18254] Add an `offset` parameter to String#unpack and String#unpack1 — "byroot (Jean Boussier)" <noreply@...>

Issue #18254 has been reported by byroot (Jean Boussier).

13 messages 2021/10/18

[#105672] [Ruby master Feature#18256] Change the canonical name of Thread::Mutex, Thread::Queue, Thread::SizedQueue and Thread::ConditionVariable to just Mutex, Queue, SizedQueue and ConditionVariable — "Eregon (Benoit Daloze)" <noreply@...>

Issue #18256 has been reported by Eregon (Benoit Daloze).

6 messages 2021/10/19

[#105692] [Ruby master Bug#18257] SystemTap/DTrace coredump on ppc64le/s390x — "vo.x (Vit Ondruch)" <noreply@...>

Issue #18257 has been reported by vo.x (Vit Ondruch).

22 messages 2021/10/20

[#105781] [Ruby master Misc#18266] DevelopersMeeting20211118Japan — "mame (Yusuke Endoh)" <noreply@...>

Issue #18266 has been reported by mame (Yusuke Endoh).

13 messages 2021/10/25

[#105805] [Ruby master Bug#18270] Refinement#{extend_object, append_features, prepend_features} should be removed — "shugo (Shugo Maeda)" <noreply@...>

Issue #18270 has been reported by shugo (Shugo Maeda).

8 messages 2021/10/26

[#105826] [Ruby master Feature#18273] Class.subclasses — "byroot (Jean Boussier)" <noreply@...>

Issue #18273 has been reported by byroot (Jean Boussier).

35 messages 2021/10/27

[#105833] [Ruby master Feature#18275] Add an option to define_method to not capture the surrounding environment — "vinistock (Vinicius Stock)" <noreply@...>

Issue #18275 has been reported by vinistock (Vinicius Stock).

11 messages 2021/10/27

[#105853] [Ruby master Feature#18276] `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)` — "ko1 (Koichi Sasada)" <noreply@...>

Issue #18276 has been reported by ko1 (Koichi Sasada).

15 messages 2021/10/28

[ruby-core:105532] [Ruby master Feature#18035] Introduce general model/semantic for immutable by default.

From: "Eregon (Benoit Daloze)" <noreply@...>
Date: 2021-10-03 12:21:51 UTC
List: ruby-core #105532
Issue #18035 has been updated by Eregon (Benoit Daloze).


I think nobody expects #freeze or #deep_freeze to ever freeze (non-singleton) classes/modules, so IMHO these methods simply not attempt that (except `SomeModule.freeze` of course).
It's the difference between state (ivars, and values of these ivars) and behavior (methods of a class).

ioquatix (Samuel Williams) wrote in #note-17:
> @eregon asserted that we should make as many of the core classes frozen by default. What's the advantage of this?

I made an extensive list of immutable classes in core here, as well as the many advantages:
https://gist.github.com/eregon/bce555fa9e9133ed27fbfc1deb181573

I'll copy the advantages here:
Advantages:
* No need to worry about .allocate-d but not #initialize-d objects => not need to check in every method if the object is #initialize-d
* internal state/fields can be truly `final`/`const`.
* simpler and faster 1-step allocation since there is no dynamic call to #initialize (instead of .new calls alloc_func and #initialize)
* Known immutable by construction, no need for extra checks, no need to iterate instance variables since no instance variables
* Potentially lower footprint due to no instance variables
* Can be shared between Ractors freely and with no cost
* Can be shared between different Ruby execution contexts in the same process and even in [persisted JIT'd code](https://www.graalvm.org/graalvm-as-a-platform/language-implementation-framework/AuxiliaryEngineCachingEnterprise/)
* Easier to reason about both for implementers and users since there is no state
* Can be freely cached as it will never change

There is a sub-category of `classes with .allocate undefined or allocator undefined, and noop initialize`, those only have the first 3 advantages, but still better than nothing.

The first advantage is I think quite important as it avoids needing to care about initialized checks for things like `klass.allocate.some_method`.

IMHO the most valuable advantages of immutable classes are that they are easier to reason about, but also they can be shared between Ractors, execution contexts in the same process (like V8 isolated contexts, I think JRuby also has those, it improves footprint and can improve warmup by JIT'ing once per process and not per context), and also in persisted JIT'd code.
Persisted JIT'd code is a feature being developed in TruffleRuby and it enables to save the JIT'ed code of a process and reuse it for the next processes.
For classes which have a literal notation, it's quite important they are immutable, otherwise one would need to reallocate one instance per execution context which feels clearly inefficient.

Given the many advantages, I think we should make more core classes immutable or `classes with .allocate undefined or allocator undefined, and noop initialize`, as much as possible.

To be shareable between execution contexts and persisted JIT'd code they need to have a well known class.
Subclassing is therefore not possible since Ruby classes are stateful.
Anyway it is highly discouraged to subclass core classes so I think that is not much of an issue.

---

For `Process::Status`, it's already in the immutable core classes, let's keep it that way.
I don't think making it subclassable is useful.
The way to create an instance for Ruby could be `Process::Status.new(*args)` and we override that `.new` to already freeze, or something like `Process::Status(*args)` or `Process.status(*args)`.

---

Regarding making user classes immutable, I think one missing piece is this hardcoded list of immutable classes in Kernel#dup and Kernel#clone.
Overriding #dup and #clone in the user class works around it, but then it doesn't work for `Kernel.instance_method(:clone).bind_call(obj)` as that will actually return a mutable copy!
It's then possible to e.g. call `initialize` on that mutable copy to mutate it, which breaks the assumption of the author of the class.

So I think we need a way for a user class to define itself as immutable (`extend Immutable` is one way, could also be by defining `MyClass.immutable?`), and for Kernel#dup and Kernel#clone to then use that to just `return self`.
If a class is marked as immutable it should be guaranteed to be deeply frozen (otherwise it's incorrect to return self for dup/clone), so we should actually deep-freeze after the custom #freeze is called from `.new`:
```
def ImmutableClass.new(*args)
  obj = super(*args)
  obj.freeze
  Primitive.deep_freeze(obj) # not a call, some known function of the VM
end
```
That way we can know this object is truly immutable from the runtime point of view as well and e.g., can be passed to another Ractor.
`Primitive.deep_freeze(obj)` would set a flag on the object so it's fast to check if the object is immutable later on.

----------------------------------------
Feature #18035: Introduce general model/semantic for immutable by default.
https://bugs.ruby-lang.org/issues/18035#change-93986

* Author: ioquatix (Samuel Williams)
* Status: Open
* Priority: Normal
----------------------------------------
It would be good to establish some rules around mutability, immutability, frozen, and deep frozen in Ruby.

I see time and time again, incorrect assumptions about how this works in production code. Constants that aren't really constant, people using `#freeze` incorrectly, etc.

I don't have any particular preference but:

- We should establish consistent patterns where possible, e.g.
  - Objects created by `new` are mutable.
  - Objects created by literal are immutable.

We have problems with how `freeze` works on composite data types, e.g. `Hash#freeze` does not impact children keys/values, same for Array. Do we need to introduce `freeze(true)` or `#deep_freeze` or some other method?

Because of this, frozen does not necessarily correspond to immutable. This is an issue which causes real world problems.

I also propose to codify this where possible, in terms of "this class of object is immutable" should be enforced by the language/runtime, e.g.


```ruby
module Immutable
  def new(...)
    super.freeze
  end
end

class MyImmutableObject
  extend Immutable

  def initialize(x)
    @x = x
  end
  
  def freeze
    return self if frozen?
    
    @x.freeze
    
    super
  end
end

o = MyImmutableObject.new([1, 2, 3])
puts o.frozen?
```

Finally, this area has an impact to thread and fiber safe programming, so it is becoming more relevant and I believe that the current approach which is rather adhoc is insufficient.

I know that it's non-trivial to retrofit existing code, but maybe it can be done via magic comment, etc, which we already did for frozen string literals.



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

In This Thread