Opened 9 years ago

Closed 8 years ago

#3867 closed enhancement (fixed)

Add alternative to array<T> for mutable variables

Reported by: Martin Sjölund Owned by: Martin Sjölund
Priority: high Milestone: 1.12.0
Component: MetaModelica Version:
Keywords: Cc: Adrian Pop, Peter Fritzson, Per Östlund

Description

Currently, a fair deal of code in OMC uses a pattern using array<T> with arrays of size 1 in order to make data types mutable. This leads to using arrayGet(something, 1) in many places.

It would be nice if we could simply declare variables mutable<T>, which would either be translated to arrays of size 1 in the run-time system, or to an immediate pointer (set lowest 2 bits to 1 or something in the header; actual pointer is ptr xor 3), or it is a special type (array1 or mutable). These options would allow to assign a mutable<T> to some field in a structure that has a mutable<T> (sharing the same variable). It could also be possible that mutable<T> was not possible to share (0 additional memory allocations). Or a mixture with pointer<T> and mutable<T>...

Note that these language features would perhaps not be nice and declarative, but we already have array<T> and it is used. This would just make code that uses this feature more maintainable.

Change History (5)

comment:1 by Martin Sjölund, 8 years ago

Some starts on the syntax for this:

  • Julia uses Lisp-style syntax for functions sort! and simply allows you to change values... It does not have pass-by reference or pointers as far as I can tell (basically what we have now with the XXXDangerous functions)
  • OCaml has references and arrays as mutable structures:
    let x : int ref = ref 3 in
    let y : int = !x (* dereference x, y is 3 *) in
      (x := !x + 1); (* update x to be the dereferences x+1, so x is now 4 *)
      y + !x (* y is the value 3; dereferencing x is 4 ; result is 7 *)
    end
    

This is something like what I wanted to do. It basically just has 3 operators:

impure function createReference<T>
  input T value;
  output Reference<T> reference;
external;
end createReference;

impure function updateReference<T>
  input Reference<T> reference;
  input T value;
external;
end updateReference;

impure function dereference<T>
  input Reference<T> reference;
  output T value;
external;
end dereference;

And would work something like this:

protected
  Reference<Integer> ref1,ref2;
  Integer i;
algorithm
  ref1 := createReference(4);
  ref2 := ref1; // aliases ref1 and ref2
  ref1 := createReference(4); // ref1 and ref2 now point to different things
  ref1 := 4; // An error
  i := ref1; // An error

We could of course have a more OCaml-like syntax if we wanted:

protected
  Reference<Integer> ref1;
  Integer i;
algorithm
  ref1 := ref(4);
  i := !ref1;
  !ref1 := i+1; // Because we already have an assignment operator

comment:2 by Martin Sjölund, 8 years ago

One question is also if we should implement it simply as external "C" and not let the compiler know or care about this datatype (similar to implementing SOME/NONE as a uniontype with type variables except here it would be an opaque uniontype I suppose).

comment:3 by Martin Sjölund, 8 years ago

Thinking about how to tag things in the run-time, I suppose just treat it similar to SOME(x), using the tag+mutable data (size: 2 pointers) in order to not truncate Integer further and make the run-time simple for this. This would make it the same size as an array of size 1 and we could even re-use the same types for this, but if we create just these 3 functions to access mutable data, we don't do bounds checking.

comment:4 by Martin Sjölund, 8 years ago

If we wanted something less built into MetaModelica, we could use a size of 3 pointers:

uniontype Reference<T>
  record REF
    T data;
  end REF;
  function create
    input T data;
    output Reference<T> ref;
  algorithm
    ref := Reference.REF(data);
  end create;
  function dereference
    input Reference<T> ref;
    output T data;
  algorithm
    data := ref.data;
  end dereference;
  function update
    input Reference<T> ref;
    input T data;
  external "C"; // Or add something to MetaModelicaBuiltin.mo to force setting the data... Or store it as a cons-cell.
  end update;
end Reference;

I think this one pretty much just works as it is.

Version 0, edited 8 years ago by Martin Sjölund (next)

comment:5 by Martin Sjölund, 8 years ago

Milestone: Future1.12.0
Resolution: fixed
Status: newclosed
Summary: Investigate alternative to array<T>Add alternative to array<T> for mutable variables
Type: defectenhancement

Fixed in PR1636. There are now two new types implemented as opaque uniontypes. The type system is the normal one, but the uniontype contains no records. Instead, external functions create either a tuple of size 1 or a SOME(x) object (boxes with ctor=0 or ctor=1). The types are Mutable<T> (only mutable) and Pointer<T> (also includes immutable references, which cannot be updated; the update function fails if trying to update an immutable reference). Pointer<T> can be used to create immutable package constants (as used for example in NFFunction). Neither version can be used to create mutable package constants.

Note: listArray({1,2,3}) can today be used in package constants (but does not create a literal; a new object is created at each access). It should have simply been forbidden and not work (it is listed as impure), but bugs made it work...

Note: See TracTickets for help on using tickets.