@@ -372,6 +372,126 @@ Well, nothing is **really** immutable in python, but you were warned.
372372We also provide :class: `returns.primitives.types.Immutable ` mixin
373373that users can use to quickly make their classes immutable.
374374
375+ Creating Modified Copies of Containers
376+ -------------------------------------- 
377+ 
378+ While containers are immutable, sometimes you need to create a modified copy
379+ of a container with different inner values. Since Python 3.13, ``returns ``
380+ containers support the ``copy.replace() `` function via the ``__replace__ ``
381+ magic method.
382+ 
383+ .. code :: python 
384+ 
385+   >> >  from  returns.result import  Success, Failure 
386+   >> >  import  copy, sys 
387+   >> >  
388+   >> >  #  Only run this example on Python 3.13+ 
389+   >> >  if  sys.version_info >=  (3 , 13 ): 
390+   ...      #  Replace the inner value of a Success container 
391+   ...      original =  Success(1 ) 
392+   ...      modified =  copy.replace(original, _inner_value = 2 ) 
393+   ...      assert  modified ==  Success(2 ) 
394+   ...      assert  original is  not  modified  #  Creates a new instance 
395+   ...  
396+   ...      #  Works with Failure too 
397+   ...      error =  Failure(" original error"  
398+   ...      new_error =  copy.replace(error, _inner_value = " new error message"  
399+   ...      assert  new_error ==  Failure(" new error message"  
400+   ...  
401+   ...      #  No changes returns the original object (due to immutability) 
402+   ...      assert  copy.replace(original) is  original 
403+   ...  else : 
404+   ...      #  For Python versions before 3.13, the tests would be skipped 
405+   ...      pass  
406+ 
407+ note ::
408+     The parameter name ``_inner_value `` is used because it directly maps to the
409+     internal attribute of the same name in ``BaseContainer ``. In the ``__replace__ ``
410+     implementation, this parameter name is specifically recognized to create a new
411+     container instance with a modified inner value.
412+ 
413+ .. warning ::
414+     While ``copy.replace() `` works at runtime, it has limitations with static
415+     type checking. If you replace an inner value with a value of a different
416+     type, type checkers won't automatically infer the new type:
417+ 
418+     .. code :: python 
419+ 
420+       #  Example that would work in Python 3.13+: 
421+       #  >>> num_container = Success(123) 
422+       #  >>> str_container = copy.replace(num_container, _inner_value="string") 
423+       #  >>> # Type checkers may still think this is Success[int] not Success[str] 
424+       >> >  #  The above is skipped in doctest as copy.replace requires Python 3.13+ 
425+ 
426+ copy.replace() `` with Custom Containers
427+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
428+ 
429+ If you create your own container by extending ``BaseContainer ``, it will automatically
430+ inherit the ``__replace__ `` implementation for free. This means your custom containers
431+ will work with ``copy.replace() `` just like the built-in ones.
432+ 
433+ .. code :: python 
434+ 
435+   >> >  from  returns.primitives.container import  BaseContainer 
436+   >> >  from  typing import  TypeVar, Generic 
437+   >> >  import  copy, sys  #  Requires Python 3.13+ for copy.replace 
438+ 
439+   >> >  T =  TypeVar(' T'  
440+   >> >  class  MyBox (BaseContainer , Generic[T]): 
441+   ...      """ A custom container that wraps a value."""  
442+   ...      def  __init__ (self inner_value : T) -> None : 
443+   ...          super ().__init__ (inner_value) 
444+   ...  
445+   ...      def  __eq__ (self other : object ) -> bool : 
446+   ...          if  not  isinstance (other, MyBox): 
447+   ...              return  False  
448+   ...          return  self ._inner_value ==  other._inner_value 
449+   ...  
450+   ...      def  __repr__ (self str : 
451+   ...          return  f " MyBox( { self ._inner_value!r } ) "  
452+ 
453+   >> >  #  Create a basic container 
454+   >> >  box =  MyBox(" hello"  
455+   >> >  
456+   >> >  #  Test works with copy.replace only on Python 3.13+ 
457+   >> >  if  sys.version_info >=  (3 , 13 ): 
458+   ...      new_box =  copy.replace(box, _inner_value = " world"  
459+   ...      assert  new_box ==  MyBox(" world"  
460+   ...      assert  box is  not  new_box 
461+   ...  else : 
462+   ...      #  For Python versions before 3.13 
463+   ...      pass  
464+ 
465+ BaseContainer ``, your custom container will automatically support:
466+ 
467+ 1. The basic container operations like ``__eq__ ``, ``__hash__ ``, ``__repr__ ``
468+ 2. Pickling via ``__getstate__ `` and ``__setstate__ ``
469+ 3. The ``copy.replace() `` functionality via ``__replace__ ``
470+ 4. Immutability via the ``Immutable `` mixin
471+ 
472+ Before Python 3.13, you can use container-specific methods to create modified copies:
473+ 
474+ .. code :: python 
475+ 
476+   >> >  from  returns.result import  Success, Failure, Result 
477+   >> >  from  typing import  Any 
478+ 
479+   >> >  #  For Success containers, we can use .map to transform the inner value 
480+   >> >  original =  Success(1 ) 
481+   >> >  modified =  original.map(lambda  _ : 2 ) 
482+   >> >  assert  modified ==  Success(2 ) 
483+ 
484+   >> >  #  For Failure containers, we can use .alt to transform the inner value 
485+   >> >  error =  Failure(" error"  
486+   >> >  new_error =  error.alt(lambda  _ : " new error"  
487+   >> >  assert  new_error ==  Failure(" new error"  
488+ 
489+   >> >  #  For general containers without knowing success/failure state: 
490+   >> >  def  replace_inner_value (container : Result[Any, Any], new_value : Any) -> Result[Any, Any]: 
491+   ...      """ Create a new container with the same state but different inner value."""  
492+   ...      if  container.is_success(): 
493+   ...          return  Success(new_value) 
494+   ...      return  Failure(new_value) 
375495
376496type-safety :
377497
0 commit comments