Open modelica-trac-importer opened 6 years ago
Comment by choeger on 3 Jun 2014 09:29 UTC Thanks for the update.
A few remarks:
We do not have an object model (i.e. abstract syntax) for our values. That means the decision of whether a pattern does match or not, will only be defined informally. I consider this a possible source of future incompatibilities. The classic approach is to define a match primitive expression and its semantics formally and then define pattern matching on top of that.
Quoting the MCPI:
However, from experience with MetaModelica in the Modelica community consisting mostly of engineers, the pattern variable feature seems to be rather difficult to grasp for people in that community, and also makes the code harder to read if one is not used to that mechanism. (But the code can be more concise and easier to read if one is used to that feature). Instead we use standard dot notation to extract field values.
I think this is a very weak reason for implementing the matching in a way practically noone else does it.
Even if it was true, that engineers are for some strange reason incompatible to pattern matching, the proposed solution implies a very special type-checking rule:
case DIVop() then eval(x.exp1) / eval(x.exp2);
case NEGop() then –eval(x.exp);
Obviously the intention is that depending on the selected pattern, either x.exp1
or x.exp
is a valid expression, but not both. This is -at least- another divergence from classical algebraic datatypes, with probably consequences for type-system soundness.
There seems to be the intention of allowing not just constructors but also (at least literal) expressions in the pattern language. There should be a definition of the matching syntax here (which probably requires a definition of some polymorphic equality)
Conclusion: The proposal seems to suffer from a minor case of the NIH syndrome ;). I'd prefer to not vary from widely accepted and tested practice without strong reason.
Comment by hansolsson on 18 Jun 2014 07:11 UTC I agree with the previous comment, and consider the case:
match x in fooBar()
case RCONST() then x.expr1;
case ADD() then x.expr1+x.expr2;
To me (and others) this half-pattern matching is confusing.
If proper pattern matching leads to problems I would propose a different variant to make it clear that these are not patterns for pattern matching and found some inspiration in Modula-2:
One could either use a different keyword for the match:
matchtype x in fooBar()
case RCONST then x.expr1;
case ADD then x.expr1+x.expr2;
alternatively add the keyword for the cases:
match x in fooBar()
if type RCONST then x.expr1;
if type ADD then x.expr1+x.expr2;
The second approach would allow us to combine patterns and type-matching:
match x in fooBar()
if type RCONST then x.expr1;
if ADD(a,a) then 2*a;
if type ADD then x.expr1+x.expr2;
The names “case type” or “type case” would be too confusing to me; but it is just naming.
Comment by petfr on 20 Nov 2014 10:11 UTC From rfranke, via e-mail:
meanwhile I also remembered the discussed "killer feature" motivating MetaModelica: It was the ability to automatically adapt existing models to changes in a used library. For instance I replaced all "dqo" with "dq0" in release 0.3 of PowerSystems - just google for dq0 and you find the right things - google for dqo and you find a lot of misleading stuff as well. Would be great if there was a standardized way to provide a script adapting all models that use an old version of PowerSystems .
Generally I think that the missing feature "conversion scripts" hinders a lot of innovation in the current development of MSL.
Comment by hansolsson on 20 Nov 2014 11:14 UTC Replying to [comment:3 petfr]:
From rfranke, via e-mail:
meanwhile I also remembered the discussed "killer feature" motivating MetaModelica: It was the ability to automatically adapt existing models to changes in a used library. For instance I replaced all "dqo" with "dq0" in release 0.3 of PowerSystems - just google for dq0 and you find the right things - google for dqo and you find a lot of misleading stuff as well. Would be great if there was a standardized way to provide a script adapting all models that use an old version of PowerSystems .
Generally I think that the missing feature "conversion scripts" hinders a lot of innovation in the current development of MSL.
I believe that is a separate issue. There is a topic for standarizing those "conversion scripts" (at least the currently useful ones, we don't need the special cases for Modelica 1->2 anymore), and pattern matching should not be a blocker for discussing that.
Lots of tools for different languages have such "refactoring" possibilities, without relying on match expressions.
For Modelica I know that Dymola and Dymola Behavior Modeling App have settings such that just changing the name of the variable (or class) automatically updates all existing uses and also creates the "conversion script", and I would assume it is similar for the other Modelica tools.
Yes, I can see that pattern matching would make it easier to write a "refactoring package" that a tool could use to implement the "conversion script" handling - but the user just wants the conversion to work automatically.
Comment by sjoelund.se on 20 Nov 2014 12:06 UTC Replying to [comment:4 hansolsson]:
Dymola and Dymola Behavior Modeling App have settings such that just changing the name of the variable (or class) automatically updates all existing uses and also creates the "conversion script", and I would assume it is similar for the other Modelica tools.
Yes, I can see that pattern matching would make it easier to write a "refactoring package" that a tool could use to implement the "conversion script" handling - but the user just wants the conversion to work automatically.
Exactly. The "conversion script" generated by Dymola will not work automatically in any other tool (that I know of). If there existed a standardised representation for the abstract syntax tree, there could be a library, say, ModelicaConversions that would make it very simple to create conversion scripts.
myConversion.mos:
ast := getLoadedAst(); // Some built-in
ast := ModelicaConversions.rename(ast=ast, fromName="Modelica.Blocks.Continuous.Integrator.k", toName="Modelica.Blocks.Continuous.Integrator.k1", classesToUpdate={"MyLibrary"});
updateLoadedAst(ast); // Modify the loaded ast to be the updated one
And the library would contain the actual code to update the abstract syntax tree. It is a very nice application anyway.
Comment by sjoelund.se on 20 Nov 2014 12:10 UTC For the OpenModelica prototype on how match-expressions and union types work: Basic-Exercise-MetaModelica.onb (The latest nightlies crash for the invalid cons-expressions, so simply don't evaluate the offending cell)
Evaluate cells by marking the cell and pressing shift+enter.
Comment by hansolsson on 20 Nov 2014 14:47 UTC Replying to [comment:5 sjoelund.se]:
Replying to [comment:4 hansolsson]:
Dymola and Dymola Behavior Modeling App have settings such that just changing the name of the variable (or class) automatically updates all existing uses and also creates the "conversion script", and I would assume it is similar for the other Modelica tools.
Yes, I can see that pattern matching would make it easier to write a "refactoring package" that a tool could use to implement the "conversion script" handling - but the user just wants the conversion to work automatically.
Exactly. The "conversion script" generated by Dymola will not work automatically in any other tool (that I know of). If there existed a standardised representation for the abstract syntax tree, there could be a library, say, ModelicaConversions that would make it very simple to create conversion scripts.
My point is that creating the ModelicaConversions library is not simple even with match expressions and a normal ast-library (and as far as I know a standarized ast-library is not on the agenda, and not planned for Modelica 3 as far as I know).
Thus we shouldn't have this topic blocking the discussion of conversion scripts, and we shouldn't try to sell match-expressions as the solution to the problem of conversion scripts.
And the library would contain the actual code to update the abstract syntax tree. It is a very nice application anyway.
I would say that the conversion scripts are not good at demonstrating an ast-library, since they sort of operate on the ghost of the ast of the last version.
During normal refactoring you can rename "dqo" to "dq0" in all classes by looking up "dqo" and atomatically replacing them all by "dq0" (that would be a good example of using an ast-library), but when using the conversion script we only have the new version and cannot find the "dqo" to replace.
Yes, a good ast-library may have the primitives to find them - but I wouldn't call it a good example of the use of the ast-library.
Similarly the procedural style of renaming items one-by-one by calling ModelicaConversions.rename is both a bit inefficient and would require that the conversion script is ordered; i.e. users could break a sequence of calls of ModelicaConversions.rename when they rearrange them to clean them up.
Modified by otter on 18 Sep 2015 10:16 UTC
Modified by otter on 18 Sep 2015 10:20 UTC
Modified by otter on 18 Sep 2015 10:20 UTC
Modified by dietmarw on 2 Dec 2015 08:31 UTC
Comment by hansolsson on 8 Mar 2016 09:35 UTC Trying to write down my comments about union.
In Haskell/ML we do things like Option=None | Some a
In Ada/Modula-2 the variant records are similar (and type-safe and allow matching).
We should have references to these and other languages in the proposal so that we can actually compare.
I don't see any languages where the same syntax is used for defining classes inside unions as for defining normal classes, and I would prefer such a separation between the variants and other local classes.
The possibilities I could see for improving this would be: 1 Use components for variants - not record 2 Use some other separator than ";" 3 Use some other tag than record for the variants
Current proposal:
union OptionType
replaceable record OptionType=Any;
record None end None;
record Some OptionType a; end Some;
end OptionType;
Variant 1:
record None end None;
union OptionType
replaceable record OptionType=Any;
None none;
OptionType some;
end OptionType;
Variant 2:
union OptionType
replaceable record OptionType=Any;
record None end None or // <--- or some other symbol but not ";"
record Some OptionType a end Some;
end OptionType;
Variant 3A:
union OptionType
replaceable record OptionType=Any;
Variant None end None;
Variant Some OptionType a; end Some;
end OptionType;
Variant 3B (Ada-like):
union OptionType
replaceable record OptionType=Any;
if None then end if;
if Some then OptionType a; end if;
end OptionType;
Comment by sjoelund.se on 10 Mar 2016 09:56 UTC Some different ways to deal with "type variables" in a (current) Modelica-like fashion:
1.
partial union Option
replaceable type T=None constrainedby Any;
record NONE
end NONE;
record Some
T value;
end Some;
end Option;
This has the problem that any function you declare is rather... useless:
function getValue
input Option opt;
output '???' value;
end getValue;
Unless you put all functions inside the union (similar to operator record):
partial union Option
replaceable type T=None constrainedby Any;
record NONE
end NONE;
record Some
T value;
end Some;
function getValue
input Option opt;
output T value = match opt case SOME(value) then value; end match;
end getValue;
end Option;
partial package Option
replaceable type T=None constrainedby Any;
partial union OptionType
record NONE
end NONE;
record Some
T value;
end Some;
end OptionType;
function getValue
input Option opt;
output T value = match opt case SOME(value) then value; end match;
end getValue;
end Option;
and 2. both have the problem that these are not really type variables. There is no polymorphism here and no re-use of functions (you will create one function for each separate type T). What MetaModelica does is:
union Option<T> // No longer partial!
record NONE
end NONE;
record Some
T value;
end Some;
end Option;
function getValue<T>
input Option<T> opt;
output T value = match opt case SOME(value) then value; end match;
end getValue;
Note that MetaModelica also allows all of 1, 2 and 3, so they are not mutually exclusive.
How to deal with subtyping can be done in different ways. What you would like to do is create aliases like:
union OptionInteger = Option(redeclare T=Integer);
union OptionInteger = Option<Integer>; // MetaModelica style
union IntegerOption = Option(redeclare T=Integer);
Should OptionInteger
and IntegerOption
be type-compatible? It is easy to deal with this with the MetaModelica style because short class declaration like this is treated as an alias for the nominal type Option
plus the type variables which also need to be subtype. So our algorithm is:
The question is if the Modelica version should treat OptionInteger
and IntegerOption
as the same type or not. They could be if the union was restricted in such a way that type variables were clearly defined as a separate concept, but I suspect it would be hard to do it in the way of 1 and 2. The style introduced in 3 is very explicit and treats type variables as separate in a way that makes it easier to write generic functions that can be re-used (but this requires tools to be able to pack all variables in a way that the record here always has the same size regardless of the type T).
If the simple style of 1 or 2 is used and we decide T is actually not a type variable, the subtyping is very simple: it is the nominal type OptionInteger
or IntegerOption
, and you need to extend either the union or package in order to add additional generic functions to these classes.
Reported by petfr on 3 Jun 2014 08:42 UTC
The MCPI-0007 Match expressions and Recursive Records design proposal is discussed at this ticket.
The most recent version v5, makes a comparison, pros and cons, between the uniontype and the case record design alternatives.
Associated files are also at the the MCPI trunk.
Migrated-From: https://trac.modelica.org/Modelica/ticket/1503