Opened 10 years ago

Closed 9 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, 9 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, 9 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, 9 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, 9 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. (Although doing ref.data := newData; would perhaps unexpectedly create a new reference and not update the old one)

Last edited 9 years ago by Martin Sjölund (previous) (diff)

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

Milestone: Future → 1.12.0
Resolution: → fixed
Status: new → closed
Summary: Investigate alternative to array<T> → Add alternative to array<T> for mutable variables
Type: defect → enhancement

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.