Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split mathics.core.assignment into mathics.core.assignment and mathics.eval.assignment #1264

Merged
merged 16 commits into from
Jan 7, 2025

Conversation

mmatera
Copy link
Contributor

@mmatera mmatera commented Jan 5, 2025

And adding docstrings and type annotations.

@rocky
Copy link
Member

rocky commented Jan 5, 2025

Thanks for undertaking this.

(I think somewhere I see Builtin misspelled - running through codespell will catch stuff like this.)

class _SetOperator:
"""

This is the base class for assignment Builtin operators.
Copy link
Member

@rocky rocky Jan 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stuff in eval probably should not have classes with objects. The focus of eval is an interpreter that knows nothing about OO. You should be able to write this code in non-OO language like C, golang, or assembly.

It is okay to pass OO objects or "self" pointers though. For these, there are object dereferences which is okay to do. Dereferencing is different from implicit OO method lookup which goes on inside classes.

In sum, this class probably belongs in either mathics.core (similar to Atom classes) or mathics.builtin.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grepping mathics.eval right now I see:

$ PAGER=cat git grep -n "class "
distance/clusters.py:8:class IllegalDataPoint(Exception):
distance/clusters.py:12:class IllegalDistance(Exception):
files_io/read.py:42:class MathicsOpen(Stream):
patterns.py:11:class _StopGeneratorMatchQ(StopGenerator):
patterns.py:15:class Matcher:
pymathics.py:21:class PyMathicsLoadException(Exception):
rules.py:149:class Dispatch(Atom):
test.py:12:class _StopGeneratorBaseElementIsFree(StopGenerator):

Exceptions and Generators are weird kinds of classes, so there may be some wiggle room here. But Dispatch(Atom) and Matcher should probably be moved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem I found is that putting this class in another place produces circular dependencies among modules. I am trying to figure out how to avoid it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easy: just get ride of the class...

@@ -223,32 +228,33 @@ def rejected_because_protected(

def unroll_conditions(lhs: BaseElement) -> Tuple[BaseElement, Optional[Expression]]:
"""
If lhs is a nested `Condition` expression,
If `element` is a nested `Condition` expression,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does "element" refer to "lhs" on line 229?

@@ -5,12 +5,12 @@
Support for Set and SetDelayed, and other assignment-like builtins
"""

from typing import Callable, List, Optional, Tuple
from typing import Callable, List, Optional, Tuple, cast
Copy link
Member

@rocky rocky Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using "cast" is a bit of a cop-out. It just postpones fixing things properly down the line. So if we can fix the problem without casting that would be nice.

But sure, I understand we have a big mess to deal with.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I am trying to figure out how to deal with functions that return Optional[...]. In any case, this will need a couple of rounds to get ready...

Copy link
Member

@rocky rocky Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have long had this annoying feeling that our classes, like BaseElement, have been a little bit wrong.

It has too many functions that are not universally applicable.

But I have deferred undertaking addressing this until I understand the code better and what to do.

And in the other direction, it might be the case that everything should have a "head" and "elements", and perhaps "value". And if we did that there would be fewer testing of types and simpler code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that only Expression and their subclasses should have head and elements properties.
On the other hand, in cases like this, where you want to use polymorphism, maybe using cast is the best solution.
In this module, I used it in two cases: one is the conversion from BaseElement to Expression. The other is to use a definition obtained from Definitions.get_definition.

In the second case, the problem is that get_definition returns None when the symbol does not have a definition. There would be two alternatives:

  • create an empty definition: This is costly and useless. We want to know as early as possible that a symbol does not have a definition, to avoid asking questions to it. Create an empty definition would introduce a lot of unnecesary overhead.
  • raise an exception: maybe this would be a more pythonic solution, just that I do not finish to get used to think of exceptions as something which is part of the standar logic of a code.

Copy link
Member

@rocky rocky Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, in cases like this, where you want to use polymorphism, maybe using cast is the best solution.

I speak from lots of experience in other programming languages: do not go down this path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. In any case, using cast is a good way to detect where the code get's fishy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, now I was able to get ride of all casts

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. I greatly appreciate the attention to improving code quality.

@mmatera mmatera marked this pull request as ready for review January 6, 2025 13:39
@mmatera
Copy link
Contributor Author

mmatera commented Jan 6, 2025

I think this is a good moment to consider merging this before continuing.

@rocky
Copy link
Member

rocky commented Jan 6, 2025

I think this is a good moment to consider merging this before continuing.

Give me a couple of days to look at. I have been meaning to see what's up with stopit first.

@mmatera
Copy link
Contributor Author

mmatera commented Jan 6, 2025

I think this is a good moment to consider merging this before continuing.

Give me a couple of days to look at. I have been meaning to see what's up with stopit first.

OK. There is no hurry. On the other hand, this is just juggling a little bit with some names and distribution of the code, and completing the documentation and type annotations.

@mmatera mmatera changed the base branch from master to removing_optional_in_get_definitions January 7, 2025 12:34
rhs: BaseElement,
evaluation: Evaluation,
) -> Optional[BaseElement]:
"tag_ /: lhs_ = rhs_"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, f_ is correct here since this is how the documentation describes the parameter. I will be putting in a PR for this and other changes.

elif name == "System`HoldPattern":
lhs = lhs_elements[0]
return lhs, rhs
# In the folliwing routines
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

??

@rocky
Copy link
Member

rocky commented Jan 7, 2025

LGTM

@rocky rocky merged commit c2e91ab into removing_optional_in_get_definitions Jan 7, 2025
2 of 9 checks passed
@rocky rocky deleted the assignment_another_round branch January 7, 2025 19:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants