moodle-qtype_formulas icon indicating copy to clipboard operation
moodle-qtype_formulas copied to clipboard

e as a built-in constant

Open dbauer-ets opened this issue 1 month ago • 13 comments

Hello Philipp,

First of all, I want to congratulate you on your outstanding work. You are a diligent, highly skilled, and truly professional contributor. I don’t think anyone is better suited than you to take care of the Formulas question type. The Moodle community owes you a great deal.

The following suggestion comes from discussion https://moodle.org/mod/forum/discuss.php?d=470926: to establish e (= 2.718…) as a built-in constant in the numeric, numerical, and algebraic answer types. This would apply only to student answers, just as is already the case with the constant pi. It would require that only E be used for exponential notation, but since this would affect only student responses, backward compatibility would not be a major issue. For algebraic answers, e would need to be disallowed as a variable, just as is currently done with pi.

What do you think?

dbauer-ets avatar Nov 22 '25 17:11 dbauer-ets

Thank you for the kind words @dbauer-ets! I think the community also owes you a lot for all the time you spend on writing the documentation and answering questions in the Moodle forums.

That sounds like a good idea. I am trying to list various points that have to be kept in mind while adding this.

  • The constant e would maybe not be available on the teacher side, because it is likely that teachers already have a variable e in their questions. This is not a big problem, because they can access the value of Euler's number using exp(1). The situation was different when I added π and pi with version 6.0.0, because legacy versions already had the function (and only the function) pi(), so it has never been possible to have a variable pi anyway. (And π was not possible either.)
  • One thing to take care of would probably be the model answer output. For numeric types (Number, Numeric, Numerical formula), the output is numeric as well, e.g. 5.436563656918 rather than 2e. OTOH, we already have the same situation for π and it did not seem to bother people. The answer 2e could be specified in the part's feedback.
  • In answers of type Number, Numeric and Numerical formula, a potential problem is the exponential notation that you mentioned above. IMHO, it would be important to keep 1.5e3 a valid notation, because it would be a big change otherwise and could possibly break backwards compatibility. My suggestion is to say that e in the middle of two numbers would always be read as part of the number, so 1.5e would be 1.5*e, because it would not be a valid exponential notation of a number. While 2e3 could, in theory, mean 2*e*3 with implicit notation, I'd argue that this is not a very common way to write that product. (Teachers could always override automatic grading anyway.) It leaves the case of 3e-2 – should that mean 3*e - 2 or rather 3*10^(-2)? That is a tricky thing.
  • Another thing to keep in mind is the combined unit field where the first token that is neither a number, nor an operator or a parenthesis is taken as the start of the units. However, I do not know any unit that would be written as e alone, so this should not be too much of a problem. (The letter e in eV would not cause problems, because the parser reads the entire token rather than just individual letters.)
  • For the answer type Algebraic formula, I think we cannot simply disallow e as a variable, as that could break compatibility for existing questions, see above regarding the comparison with pi. With e being the fifth letter of the alphabet, it is not unlikely for it to appear in exercises. One possible solution could be as follows: if a question already uses e as a variable, the constant e would not be available (not even for the student). If no variable e has been defined in the question, the usage of the symbol e as a variable in a student answer would be wrong, so considering it as the constant e would make sense.
  • Maybe there is even a possibility to extend the idea above to the teacher's side, i. e. as soon as they define a variable e in their question, they lose the ability to access the constant e (or they would have to access it using the prefix operator, writing \e). That is similar to functions. Teachers can define a variable sin, but if they do, they will have to write \sin(π/2) instead of sin(π/2); the latter would otherwise min sin*(π/2) with sin being the content of their variable.

Please feel free to add other things that you think might cause problems.

PhilippImhof avatar Nov 23 '25 16:11 PhilippImhof

Hello Philipp,

  • The constant e would maybe not be available on the teacher side, because it is likely that teachers already have a variable e in their questions. This is not a big problem, because they can access the value of Euler's number using exp(1). The situation was different when I added π and pi with version 6.0.0, because legacy versions already had the function (and only the function) pi(), so it has never been possible to have a variable pi anyway. (And π was not possible either.)

Here’s my understanding of how pi is handled.

Teachers

You have added both pi and π, which is excellent.

The function pi() was — and still is, unless it has been changed — equal to 3.14… when called without an argument. With an argument, it returns 3.14… × argument. For example, a = pi() assigns the value 3.14… to a, and b = pi(3) assigns the value 3.14… × 3 to b.

Students

You have added π and kept pi, which is excellent.

dbauer-ets avatar Nov 24 '25 02:11 dbauer-ets

  • One thing to take care of would probably be the model answer output. For numeric types (Number, Numeric, Numerical formula), the output is numeric as well, e.g. 5.436563656918 rather than 2e. OTOH, we already have the same situation for π and it did not seem to bother people. The answer 2e could be specified in the part's feedback.

I agree with you.

It is the same, for example, for 0.1, which cannot be represented exactly in binary floating-point; √2, which has an infinite non-repeating decimal and binary expansion; and so on.

dbauer-ets avatar Nov 24 '25 02:11 dbauer-ets

  • In answers of type Number, Numeric and Numerical formula, a potential problem is the exponential notation that you mentioned above. IMHO, it would be important to keep 1.5e3 a valid notation, because it would be a big change otherwise and could possibly break backwards compatibility. My suggestion is to say that e in the middle of two numbers would always be read as part of the number, so 1.5e would be 1.5*e, because it would not be a valid exponential notation of a number. While 2e3 could, in theory, mean 2*e*3 with implicit notation, I'd argue that this is not a very common way to write that product. (Teachers could always override automatic grading anyway.) It leaves the case of 3e-2 – should that mean 3*e - 2 or rather 3*10^(-2)? That is a tricky thing.

I may be mistaken, but in my opinion there are a limited number of, if any, situations where backward compatibility with students’ answers really matters. The fact that some students used e for exponential notation in the past does not seem to me to be an obstacle. I would say that from now on students should use E for exponential notation—which is in fact the recommended practice—and that they may now use e for Euler’s constant. If I tell my students that they must now use E for exponential notation and e for Euler’s constant, I think they will understand easily.

If we consider the 26 letters of the Latin alphabet and the 24 letters of the Greek alphabet, in both uppercase and lowercase, we have 100 unique characters, plus a few others to represent all scientific constants and variables. This can be restrictive. Many scientific constants and variables are already represented by the same symbols (see below for e and E), so it is important to avoid using different symbols for the same entity.

For example, we represent Euler’s constant with the letter e and would not want to use E, because that would create confusion. Similarly, if we choose to represent exponential notation with the letter E, we should avoid encouraging its representation by e.

  • e Euler’s constant ≈ 2.71828…
  • e Elementary charge (electron/proton) ≈ 1.602×10⁻¹⁹ C
  • E Exponential notation = 10ⁿ
  • E Young’s modulus (≈ 200 GPa for steel)
  • E Energy (e.g., E = mc²)
  • …etc.

dbauer-ets avatar Nov 24 '25 04:11 dbauer-ets

  • Another thing to keep in mind is the combined unit field where the first token that is neither a number, nor an operator or a parenthesis is taken as the start of the units. However, I do not know any unit that would be written as e alone, so this should not be too much of a problem. (The letter e in eV would not cause problems, because the parser reads the entire token rather than just individual letters.)

There are also the following, but I do not think they are problematic:

  • 1 erg = 10⁻⁷ J — a CGS unit, which is no longer commonly used today.
  • E, the SI prefix exa, e.g., 1 EJ = 1 exajoule = 10¹⁸ joules.

dbauer-ets avatar Nov 24 '25 04:11 dbauer-ets

  • For the answer type Algebraic formula, I think we cannot simply disallow e as a variable, as that could break compatibility for existing questions, see above regarding the comparison with pi. With e being the fifth letter of the alphabet, it is not unlikely for it to appear in exercises. One possible solution could be as follows: if a question already uses e as a variable, the constant e would not be available (not even for the student). If no variable e has been defined in the question, the usage of the symbol e as a variable in a student answer would be wrong, so considering it as the constant e would make sense.

Tricky, but I think you are right. I don’t see any other way to handle this.

dbauer-ets avatar Nov 24 '25 04:11 dbauer-ets

  • Maybe there is even a possibility to extend the idea above to the teacher's side, i. e. as soon as they define a variable e in their question, they lose the ability to access the constant e (or they would have to access it using the prefix operator, writing \e). That is similar to functions. Teachers can define a variable sin, but if they do, they will have to write \sin(π/2) instead of sin(π/2); the latter would otherwise min sin*(π/2) with sin being the content of their variable.

I don’t think it’s necessary to go that far. I doubt that a scientist would knowingly use both a variable e and the constant e in the same problem, on paper, unless the meanings are obvious from the context. In computing, we still can’t rely on context (though that may come with AI). I think a teacher can more simply use, for example, e₁ or even E for the variable, while e is always reserved for Euler’s constant. Having both e and \e seems very confusing.

As for using sin as a variable, that seems like heresy! 🙂 I have never seen it, and I hope I never do. 🙂 In my opinion, all built-in functions should be protected and should never be allowed to be defined as variables.

dbauer-ets avatar Nov 24 '25 04:11 dbauer-ets

Hello Dominique

The function pi() was — and still is, unless it has been changed — equal to 3.14… when called without an argument. With an argument, it returns 3.14… × argument. For example, a = pi() assigns the value 3.14… to a, and b = pi(3) assigns the value 3.14… × 3 to b.

I agree with you; the idea of having a function like that always seemed very strange and counterintuitive to me. The reason behind it was probably that Formulas did not have its own logic or interpretation, it just did some substitutions and then piped the input to PHP. So pi() would then be taken care of by PHP, where that same function also exists and worked in the same way. (As from PHP 8.0, calling pi() with an argument throws an error, it did not earlier, that's why pi(3) was possible.) There was also a constant pi, but it could not be used by teachers, because the substitute_constants_by_placeholders() function was never used in the evaluation of assignments or expressions. It was only used then evaluating student answers or algebraic formulas.

I may be mistaken, but in my opinion there are a limited number of, if any, situations where backward compatibility with students’ answers really matters. The fact that some students used e for exponential notation in the past does not seem to me to be an obstacle. I would say that from now on students should use E for exponential notation—which is in fact the recommended practice—and that they may now use e for Euler’s constant. If I tell my students that they must now use E for exponential notation and e for Euler’s constant, I think they will understand easily.

You are probably right. Backwards compatibility for answers is mainly needed during open or ongoing attempts. If interpretation is changed during an attempt, a student's answer might become wrong between the moment they entered it and the moment they review their attempt.

However, I still see a problem with forcing the capital E, because the notation 1.5e3 is widely spread. All programming languages that I know accept it (especially popular languages like Javascript, PHP or Python) and the same is true for computer algebra systems and computation software (Maple, Mathematica, Wolfram Alpha, Maxima, Matlab etc.). The main problem would be that 1.5e-3 would be wrong, but not invalid, i. e. the student would not be warned that his input is not understood in the way they meant it. (They would still see the MathJax preview showing 1.5 · e – 3 instead of 1.5 · 10⁻³, though.)

I don’t think it’s necessary to go that far. I doubt that a scientist would knowingly use both a variable e and the constant e in the same problem, on paper, unless the meanings are obvious from the context. In computing, we still can’t rely on context (though that may come with AI). I think a teacher can more simply use, for example, e₁ or even E for the variable, while e is always reserved for Euler’s constant. Having both e and \e seems very confusing.

Good point. Yes, using a variable e in a situation where one also works with Euler's number would really be strange.

As for using sin as a variable, that seems like heresy! 🙂 I have never seen it, and I hope I never do. 🙂 In my opinion, all built-in functions should be protected and should never be allowed to be defined as variables.

🙂

The idea behind the concept is that hard protection of function names is dangerous, because it is "retroactive". With the legacy version, every new function could break backwards compatibility by "forbidding" the use of variables that used to be fine before. If we ever introduce sen as an alternative to sin (as requested by some Spanish speaking users), then all questions that use sen as a variable would stop working. And I can imagine that a Spanish speaking teacher could probably use the variable sen to store some sine value, especially in a situation where they have to use sin for the function name.

The other idea behind it was that teachers could "block" certain functions by defining them as variables. A teacher could, for example, define sin = 0 and thus make the sine function inaccessible to students, as students are not allowed to use variables. OTOH, the teacher could still access the sine function in their calculations by writing \sin(...).

PhilippImhof avatar Nov 24 '25 07:11 PhilippImhof

Hello Philipp,

I just want to share my point of view, which may differ from yours. Let’s hope that some clarity will emerge from our discussion. My tone is direct so that my message is clear, but I am not being accusatory. It’s possible that we may have differing opinions — in the end, it’s not such a big deal. 🙂


Multiple authoritative style guides and language standards explicitly warn against shadowing or reusing names of built-in functions, constants, or types.

Below are solid references for PHP, Python, and JavaScript, plus general software-engineering guidelines.


1. General Software Engineering References

Clean Code — Robert C. Martin (2008)

Chapter “Meaningful Names” explicitly warns against misleading names and shadowing:

  • “Avoid using names which cause readers to mentally map one concept onto another.”
  • Shadowing built-ins creates exactly this confusion.

The Pragmatic Programmer — Hunt & Thomas

Recommends avoiding name collisions with language features, as they reduce clarity and increase bug risk.


2. PHP

PSR-1: Basic Coding Standard (PHP-FIG)

Section “Naming Conventions”:

  • Names must not conflict with PHP reserved keywords and should avoid confusing naming.

Although PSR does not list every built-in function, the principle is that code must be readable and unambiguous.

PHP Manual: Variable Naming Rules

The manual states that variables must not collide with reserved keywords and warns about behavior when a bare word is treated as a constant. While not directly saying “do not reuse function names,” it strongly discourages constructs that create ambiguity.

Zend Framework / Symfony Coding Standards

Both insist on avoiding names that conflict with PHP internals, precisely to avoid ambiguity.


3. Python

PEP 8 – Python Style Guide

Section “Naming conventions”:

  • “Never use the names of built-in types or functions for your own variables.”

This is the clearest and strongest reference.

Examples: do not use list, str, sum, min, max, open, etc.

Python Tutor / Official Docs

The Python documentation on namespaces also warns that shadowing built-ins changes program behavior.


4. JavaScript

MDN (Mozilla Developer Network)

MDN’s documentation on naming warns against shadowing built-ins such as Array, Math, Map, JSON, etc.

Airbnb JavaScript Style Guide

Section “Avoid shadowing built-ins”:

  • “Do not shadow built-in objects or functions. This can lead to confusion and errors.”

Google JavaScript Style Guide

Says the same: avoid naming collisions with standard library identifiers.


5. Why all these standards agree

Shadowing built-ins creates:

  • Ambiguity (“is sin a variable or the trig function?”)
  • Hard-to-find bugs (especially in Python)
  • Reduced readability
  • Unexpected behavior when built-ins are overwritten

This is why every major style guide considers it bad practice.


✔️ Summary (with references)

Language Standard / Guide Explicit Guidance
PHP PSR-1, Symfony, Zend standards Avoid ambiguous or conflicting names
Python PEP 8 Never shadow built-in names
JavaScript Airbnb JS Guide, Google JS Guide, MDN Avoid shadowing built-in objects/functions
General Clean Code, Pragmatic Programmer Names must be clear, non-misleading


By continuing to allow the use of built-in names as variable names, you preserve backward compatibility of a very bad practice — one that is probably not widely used anyway. I doubt many people have ever written something like sin = π; x = sin(2 sin);. And if someone did, it would actually be doing them a favour to point out that their code is awful.

By continuing to allow built-in names to be used as variable names, you promote bad practice, ambiguity, reduced readability and confusion... for a very poor reason.


My recommendation is to forbid the use of any built-in name. This approach is simple and effective, and I consider the arguments against it to be rather frivolous.

dbauer-ets avatar Nov 25 '25 01:11 dbauer-ets

Hello Dominque, and thanks for your input!

I do absolutely agree with you that reusing names of built-in functions or constants is bad practice. However, I think that my role is not to educate our users, but rather to make things work and, even more, to keep things working. (We know that users do not read style guides, just as they often do not read documentation…)

The examples you cite actually come to the same conclusion as I do:

PHP

PHP does not have the problem as much as Formulas, because variables always start with the $ sign, so variable names and function names can never clash. For the rest, they offer namespaces. Inside my own namespace, I can do whatever I want, e.g.

<?php
namespace foo;

function strlen($s) {
    return 42;
}

echo strlen("hello"); // 42
echo "\n";
echo \strlen("hello"); // 5
?>

Of course, that would be bad practice, but PHP does not forbid it. And it makes sense not to, because it makes it easier to add new functions at a later time. One example: there was no intdiv() function prior to PHP 7. But integer division is a thing, so it is safe to assume that some people had implemented such a function in their projects. Without the option of namespaces, such a project would stop working when the server is upgraded from 5.6 to 7.0. (Incompatibilities are to be expected when the major version changes, but still. The developer might be long gone and users just need a solution to get things working again.)

Python

In Python, it is even possible to redefine existing functions:

def len(a):
    return 42

print(len("hello")) # 42

It is, of course, not possible to redefine reserved words like if etc., but these do not change very often. And the last time they added a new reserved word (AFAIR it was type in 3.12), they marked it as a "soft" keyword, which means that in most situations, you can have a variable type = 5 in your script and it will still work. Also, Python uses namespaces, which reduces the risk of clashes.

Again, it is not very unlikely for people to have a variable type in their scripts, as it is a common and short word. If the Python folks had chosen a rigid policy, all scripts using such a variable would have stopped working. That would probably have given some serious backlash.

Javascript

In modern Javascript, many of the built-in things are protected, but it is still possible to do even very, very strange things, e. g.

String.prototype.toUpperCase = function() {
  return "Sorry, no!";
};

console.log("hello".toUpperCase()); // Sorry, no!

For the rest, Javascript also allows using namespaces (via modules) to avoid clashes. People overwriting globals used to be a big problem in the old Javascript days…

Formulas

By continuing to allow the use of built-in names as variable names, you preserve backward compatibility of a very bad practice

I beg to differ: as long as Formulas does not define a built-in constant e, it is not bad practice at all to use e as a variable, especially not in contexts that are far away from exponentials. In general algebra, e is often used as a simple variable to work with – why not? Students don't even know that there is something special about that letter. Otherwise, should one also avoid other classic symbols with special meanings like c, g or G?

It is one of my major concerns that we do not break existing questions, unless it is absolutely necessary. The reason is simple: as a teacher, I would never want to use Formulas if I had to constantly monitor its development just to make sure that my questions keep working as they did when I wrote them. I see the process: school admins install an update that adds a function or constant, probably without even telling teachers about it, and – boom – the test a teacher has prepared for today crashes at question 3. (Needless to say: people do not read change logs either…)

The day I would have to rewrite my questions (and check every single one of them!) just because the developer decided they wanted to add something without caring about my prior work, I would turn my back on their product and rather spend that time on migrating everything to STACK.

— one that is probably not widely used anyway. I doubt many people have ever written something like sin = π; x = sin(2 sin);. And if someone did, it would actually be doing them a favour to point out that their code is awful.

You are right, that is certainly not a common practice, especially because sin was not even available as a variable before 6.0. But I can imagine some teachers use wurzel or racine to store the square root of a number. As we do not plan on adding functions wurzel() or racine() that is of no importance. But what about other words or abbreviations in other languages? Can we be sure that they do not, by pure chance, match the name of the next function that we are trying to add?

By following the strict policy you are advocating, every new function could break existing questions, even if the teacher never intended using that function in their question. That's what I want to avoid. It brings the "downside" of allowing people to do stupid things, yes. But it is a safe decision. And, as I said, there could be situations where one would like to make a function unavailable to students, so I see it as a potentially valuable feature in certain cases.

As I said above: I do not want to educate our users. Would I recommend them to use sin = 5 as a variable? No, surely not. Do I think it is good to forbid it? No, not really. Do I think it is of any use to tell them their code is awful? No, probably not. They would not want to hear it.

It would, of course, be possible to limit the \ syntax only to newer functions, because it is not needed for backwards compatibility (old functions could never be used as variables). But that would have added unnecessary complexity (read: increased risk of bugs) and thus more work. IMHO, adding that notation was a necessary condition in order to be able to safely add new functions at any later time.

By continuing to allow built-in names to be used as variable names, you promote bad practice, ambiguity, reduced readability and confusion... for a very poor reason.

I do not think that I promote it – not more than Python promotes overriding built-in functions just because it does not throw an error when people do it.

My recommendation is to forbid the use of any built-in name. This approach is simple and effective, and I consider the arguments against it to be rather frivolous.

Not if the meaning of "built-in" can change on every release. That strict policy would be possible if a clear list of "forbidden" identifiers had been prepared and published on the first release of Formulas. It is too late now. If we want to have a clean thing, we would probably have to start "Formulas NG" with a precise and future-proof design. I had that in mind, but I decided to rather put my energy into the existing plugin, even if that meant that I would have to follow certain decisions that might go back to Hon Wai Lau's time. My understanding is that Formulas was not meant to be as versatile and powerful as it is now, but I may be wrong. Since then, both Formulas and Moodle have come a long, long way…

PhilippImhof avatar Nov 25 '25 08:11 PhilippImhof

One example: there was no intdiv() function prior to PHP 7. But integer division is a thing, so it is safe to assume that some people had implemented such a function in their projects. Without the option of namespaces, such a project would stop working when the server is upgraded from 5.6 to 7.0. (Incompatibilities are to be expected when the major version changes, but still. The developer might be long gone and users just need a solution to get things working again.)

It would probably have been better to take a more recent example: str_contains() was added in 8.0. As such a function is useful, it's likely that some projects had their own implementation of it prior to 8.0, so their scripts would have stopped working when upgrading from 7.4 to 8.0. Thanks to namespaces, they did not.

PhilippImhof avatar Nov 25 '25 08:11 PhilippImhof

Philipp,

You advocate backward compatibility to justify some changes, yet you disregard it for others (INF and more).

Teachers do not read documentation or change logs... We are not here to educate them... They will simply turn to STACK... Your arguments are not convincing.

The Formulas language is not a general-purpose language. We can and should impose clear, strict rules so that it remains simple and efficient.

The proper way to proceed is:

  1. Announce that a function or feature is deprecated.
  2. Clearly mark it as obsolete, while still allowing it to work for at least one major version.
  3. In the next major version, remove the deprecated function or feature entirely.
  4. Ensure the documentation explains both the deprecation process and the recommended alternatives.

We should prioritize coherence and simplicity over legacy constraints. And you cannot invoke backward compatibility selectively, whenever it happens to support a chosen position.

Having sin as a variable as well as sin and \sin as functions is completely chaotic — not a good idea, not a good solution.

I am vetoing against it.

The only acceptable exception is e, which historically and universally serves three roles:

  • as a variable,
  • as a function (the exponential),
  • and as exponential notation itself.

This is the only special case that should be considered, and no other built-in functions should be allowed as variables.

dbauer-ets avatar Nov 26 '25 20:11 dbauer-ets

Dear Dominique

You advocate backward compatibility to justify some changes, yet you disregard it for others (INF and more).

  • The change with INF was a necessary change in order to reliably detect certain errors across different versions of PHP.

  • Those errors had the potential to break questions or even an entire quiz. Such things have happened; I know it, because I had to write the fix to solve the problem.

  • You had a situation yourself where this exact change allowed you to discover a programming error you made while defining a question. Back then, you thought that – I quote – this was "excellent" that it demonstrated "the high quality of [my] work". Apparently, things have changed since then.

  • INF was never a "feature". It could not be actively used. It could just appear as a result of a calculation, and in many cases that was probably rather due to an error than intentional. It was not documented anywhere. There were no unit tests covering it. I simply did not think of it when making the change. (That's why unit tests are important. I have added a lot of them.) If that change has broken things for the (probably very few) users who were relying on it, then I'll have to accept the blame and apologise. As mentioned in #294, it is possible to bring it back once we drop support for PHP 7.4.

Teachers do not read documentation or change logs... We are not here to educate them... They will simply turn to STACK... Your arguments are not convincing.

What arguments would it take to convince you after you have already decided how things had to be? I get the impression that you want to have it your way, because it is the only way that will ever make sense. If something does not make sense for you, nobody should be allowed to do that.

Also, I did not say that people would switch to STACK. I said that I would do it, if I was a user of a product that did not value backwards compatibility. Do you think companies would continue to use Excel if they had to check (and possibly adapt) all of their existing spreadsheets on every update?

The proper way to proceed is:

In my world, there is generally more than just one proper way to proceed.

  1. Announce that a function or feature is deprecated.
  2. Clearly mark it as obsolete, while still allowing it to work for at least one major version.
  3. In the next major version, remove the deprecated function or feature entirely.
  4. Ensure the documentation explains both the deprecation process and the recommended alternatives.

That procedure does not seem acceptable to me.

It could be, if people could just stick with their version and be happy. We could then say: "Look, you can continue with version X with the current feature set. Or you can, in the long run, update to version Y in order to have newer, better features." But the thing is that people will have to upgrade sooner or later, simply because old versions will not be compatible with newer versions of Moodle anymore. Some schools have thousands of questions, sometimes old questions created by teachers who do not even work there anymore. Although we do not owe them anything, we must not let them down.

Also, you suggestion would be extremely inefficient, because it means that first I have to implement a new feature while maintaining backwards compatibility (i. e. in a more complicated way) and then, some time later, I have to strip that backwards compatibility I have struggled to put there, possibly creating new bugs. I would then either leave a (likely) suboptimal implementation or re-implement the feature a second time in the clean way that could have been used if backwards compatibility had not been an issue in the first place.

We should prioritize coherence and simplicity over legacy constraints. And you cannot invoke backward compatibility selectively, whenever it happens to support a chosen position.

I do not invoke backward compatibility selectively. If you took some time to look at the changes I made in the last months, you could easily see that there were many things, that I re-implemented in order to match undocumented legacy behaviour. The most recent ist #288. Did you know that legacy code allowed spaces around the list variable in a multi-choice answer? I did not. And I think it makes absolutely no sense. IMHO it was just possible because of a sloppy regular expression.

As I said earlier: many things would have been so much easier for me, if I had not focused on backwards compatibility.

Having sin as a variable as well as sin and \sin as functions is completely chaotic — not a good idea, not a good solution.

I am vetoing against it.

  • You cannot veto against it, because it is already there. Since version 6.0.0.
  • The fact that you did not notice it clearly shows that there is no need to veto against it, because it did not harm you (or anybody) in any way. It does not take anything from anybody, while offering new possibilities.
  • It is a safe way to ensure that we can add new functions (or possibly a new constant) at any later time without breaking existing questions. Without such a mechanism, it would be absolutely unthinkable to implement e as a constant. (Or rather, the introduction of that constant would make it necessary to add such a syntax unless we were to break all existing questions using the variable e.)
  • It follows accepted principles, e. g. PHP uses a similar syntax for the same purpose.

I should probably not have given the sine function as an example, because it really seems to trigger you and you somehow cannot seem to get over that one single example. You are stuck with this idea and everything I say will now automatically land in the "bullshit drawer".

Yes, it may seem stupid to define a variable sin in a question. But why is that our (your) problem? How does it harm us if people do things that we consider stupid? Do you really think that millions of teachers are now going to do that? (And even if they did, why should we care…)

And, funny enough, I even outlined an example where defining a variable sin, although stupid in most contexts, could make sense:

  • Take the following problem: "Calculate the sine of …." Answer type is "Numerical formula", because you want students to be able to write things like sqrt(3)/2.
  • Now that functions are allowed in the answer field, students could simply write sin(…) and the automatic correction would award full marks, manual intervention from a teacher is needed. (I always check my students' answers manually, but I am not here to judge the work of other teachers, as that's not my business.)
  • Thanks to the prefix notation, the teacher could define sin = 0 (or any other value), so the student can no longer use sin in their answer. Yet, the teacher can still use \sin(…) in their model answer.

The only acceptable exception is e, which historically and universally serves three roles:

Only acceptable exception? Who defines that? You and you alone?


Anyway, thanks for your comments. They made me realise that I will need a break and take some time to find out whether I really want to expose myself to all of this in the long run.

PhilippImhof avatar Nov 27 '25 07:11 PhilippImhof

Philipp,

You are doing extraordinary work. A part of the Moodle community is very grateful to you, myself first. I say “a part” because there will always be teachers who take everything for granted, who think everything is owed to them, who have no idea how much volunteer work goes into Moodle and its plugins, and who don’t care anyway.

You are a star of the Formulas question type, a true warrior. I hope you will continue its development for many years to come.

There are no doubt two schools of thought: one that favours backward compatibility, and another that gives it less importance. In the present case, I think backward compatibility should not prevail, for two reasons:

  • It introduces a complexity (sin as a variable, sin and /sin as functions) that most teachers will not understand.
  • The majority of teachers complain about everything. Believe me, I’ve been in this field for over 25 years. Sorry for the blunt language, but many teachers are arrogant idiots. If they are not able to read the release notes, that’s their problem. I don’t think we need to worry about them. Let them complain, let them abandon the Formulas question — the fewer of them, the better.

You also mentioned two other reasons: (1) a teacher may intentionally (or unintentionally) block a function by declaring it as a variable, and (2) we don’t yet know the names of functions that may be added in the future.

To reason (1), I would say that blocking a function might occasionally be useful, but I believe we can achieve this in other ways.

To reason (2), I would say it doesn’t really matter. We can announce that the name of the new function will henceforth be reserved, and give teachers time to rename their variable if necessary. Then we move forward.

Personally, I would rather have a list of names to avoid (sin, cos, etc.), even if the list evolves and I occasionally need to update my questions, than allow sin to be used as a variable at all. Not to mention that if sin is a variable, teachers could ask students to use it as such in their answers, which is pedagogically questionable.

I hope you understand my position: I think it’s a bad idea. I would suggest you keep in mind that your efforts to ensure backward compatibility will likely not be appreciated by the arrogant teachers who will complain no matter what. I don’t think we need to accommodate teachers who don’t read the documentation and who go against basic good coding practices. Neither solution is perfect, but one may be simpler than the other.

You are the one who has taken charge of the development of the question type’s code. So I leave the last word, the final decision, to you. Do what you believe is best. :-)

dbauer-ets avatar Nov 27 '25 19:11 dbauer-ets

Hello Philipp,

I’m not criticizing you, on the contrary, I think your work is extraordinary. That doesn’t prevent us from having differing ideas from time to time. It’s not a big deal; it might even be a good thing to have different viewpoints on a given topic.

I tend to look at things from the users’ perspective, but that doesn’t mean I’m always right. Communication between programmers and users is, unfortunately, often quite poor. Several programmers sit in their ivory tower and look down on users. It’s not uncommon. The role I give myself is to prevent absurdities from making their way into the Formulas question type. I’m not saying that you’ve done anything absurd, not at all. I’m just saying that I would point it out if I thought something didn’t make sense. I ask you questions and challenge you on certain points. I believe that’s not a bad thing in itself. I try to offer you a viewpoint that differs from yours.

Regarding the use of built-in function names as variable names, you have made some valid points, and I have made others. The Formulas question type has worked perfectly well for years without this feature. You think it’s a necessary improvement, perhaps. Some of your arguments do make good sense, which is great.

So please don’t feel hurt or insulted. That is absolutely not my intention. I genuinely think your work is outstanding.

So let’s move on to other things, if you like.

dbauer-ets avatar Nov 28 '25 04:11 dbauer-ets

Take the following problem: "Calculate the sine of …." Answer type is "Numerical formula", because you want students to be able to write things like sqrt(3)/2. Now that functions are allowed in the answer field, students could simply write sin(…) and the automatic correction would award full marks, manual intervention from a teacher is needed. (I always check my students' answers manually, but I am not here to judge the work of other teachers, as that's not my business.) Thanks to the prefix notation, the teacher could define sin = 0 (or any other value), so the student can no longer use sin in their answer. Yet, the teacher can still use \sin(…) in their model answer.

Now that functions are allowed in the answer field...

Weren’t functions always allowed in the answer field?

... so the student can no longer use sin in their answer...

If sin is a variable, how can students be prevented from using it in their answer? Wouldn’t it just produce a syntax error, depending on what exactly they type, or an incorrect answer because the value of sin is equal to 0 or some other value?

By the way, you also seem to like the sin example. :-)

dbauer-ets avatar Nov 28 '25 05:11 dbauer-ets

Take the following problem: "Calculate the sine of …." Answer type is "Numerical formula", because you want students to be able to write things like sqrt(3)/2. Now that functions are allowed in the answer field, students could simply write sin(…) and the automatic correction would award full marks, manual intervention from a teacher is needed. (I always check my students' answers manually, but I am not here to judge the work of other teachers, as that's not my business.) Thanks to the prefix notation, the teacher could define sin = 0 (or any other value), so the student can no longer use sin in their answer. Yet, the teacher can still use \sin(…) in their model answer.

Now that functions are allowed in the answer field...

Weren’t functions always allowed in the answer field?

Yes, that was a poor choice of words. What I meant was: Now that the teacher has set the answer type to "Numerical formulas", functions are allowed and the student could abuse this by writing sin(...).

If sin is a variable, how can students be prevented from using it in their answer? Wouldn’t it just produce a syntax error, depending on what exactly they type, or an incorrect answer because the value of sin is equal to 0 or some other value?

Yes, it would lead to an error, because students are not allowed to use variables unless the answer type is "Algebraic formula". And, as you correctly saw it, even if they were allowed to, the result would be wrong, because sin(...) would be 0 * ....

By the way, you also seem to like the sin example. :-)

Yes, I love it. Because it is simple, without being trivial. In problems involving trigonometric functions, it often makes sense to use "Numerical formula", because of the square roots that appear in some exact values.

I could have made a similar argument for problems involving fact, sqrt or ln, but in all these cases the obvious solution for the teacher would be to use the answer type "Number" or "Numeric" instead of "Numerical formula".

As an alternative example, I could imagine students have to apply the change-of-base rule for logarithms to rewrite lg(...) as an expression involving ln only, i. e. ln(…)/ln(10). Blocking the lg function will make it impossible to evade the problem by simply repeating lg(…) as the answer. (Even if I think that teachers should manually check that in a real test.)

Note that when I first had the idea of selectively blocking functions, I was also planning to allow multi-argument functions for students, something that I have put on hold when we allowed the comma as a decimal separator. But I thought that if stuff like ncr(n, k) or log(arg, base) were possible, teachers could feel the need to disallow some of them depending on the problem.


Apart from that, I agree with you that it would have been perfectly acceptable to simply say: "As long as you use the variable xxx in your question, you will not be able to use the newly added function xxx." Obviously, the teacher hadn't needed that function before, so they would not have had to change anything in their existing questions.

PhilippImhof avatar Nov 30 '25 11:11 PhilippImhof