Opened 4 years ago
Last modified 4 years ago
#17958 new defect
implement declare_var, deprecate (None)var
Reported by: | rws | Owned by: | |
---|---|---|---|
Priority: | major | Milestone: | sage-6.6 |
Component: | symbolics | Keywords: | |
Cc: | nbruin, kcrisman | Merged in: | |
Authors: | Reviewers: | ||
Report Upstream: | N/A | Work issues: | |
Branch: | Commit: | ||
Dependencies: | Stopgaps: |
Description (last modified by )
Functions returning a value should not have side effects, var
does. In #17447, comment 23 Nils Bruin proposed to separate both usages of var
by introducing declare_var
: this should behave exactly like var
without return value, and var
should not put the var handle in the globals
list but should act like SR.var
. If we want the following behaviour:
declare_var('x')
==var('x')
as before but returningNone
var('x')
prints deprecation message, returns variable as before; error after deprecation periody = var('x')
as before (but without globals), NO deprecation message
we certainly need the preparser to recognize 2/3, and to replace (2) with declare_var('x'); deprecation(...); x
and (3) with y = SR.var('x')
. Secondly, there is a different docstring needed with var
when compared with declare_var
.
This and the same with functions is the most annoying problem for people doing calculus in Sage.
Change History (69)
comment:1 Changed 4 years ago by
- Summary changed from implement declare_var to implement declare_var, deprecate (None)var
comment:2 Changed 4 years ago by
- Cc nbruin added
- Description modified (diff)
comment:3 Changed 4 years ago by
comment:4 Changed 4 years ago by
Not really need but:
sage -t src/sage/symbolic/expression.pyx # 424 doctests failed sage -t src/sage/symbolic/assumptions.py # 51 doctests failed sage -t src/sage/symbolic/callable.py # 48 doctests failed sage -t src/sage/calculus/riemann.pyx # 126 doctests failed sage -t src/sage/calculus/calculus.py # 81 doctests failed sage -t src/sage/calculus/tests.py # 29 doctests failed sage -t src/sage/calculus/wester.py # 29 doctests failed sage -t src/doc/en/prep/Advanced-2DPlotting.rst # 24 doctests failed sage -t src/doc/en/prep/Quickstarts/Multivariable-Calculus.rst # 38 doctests failed sage -t src/doc/de/thematische_anleitungen/sage_gymnasium.rst # 31 doctests failed sage -t src/sage/functions/piecewise.py # 25 doctests failed
That's those over 20 in symbolic
,calculus
,doc
,functions
.
EDIT: grep for 'sage: var(' shows 338 hits in 67 files...
comment:5 follow-up: ↓ 7 Changed 4 years ago by
I should have looked sooner: the catch is, I removed the globals
injection in calculus/var.pyx:var()
and the variables get still injected. And indeed the same with SR.var()
, so that has side effects too and is no cure for the matter.
EDIT: Noo, that's wrong, it's the expression on the lhs that gets injected!
I still have to find a case where injection of the variable itself matters.
comment:6 Changed 4 years ago by
I think I don't understand what you mean:
sage: SR.var('xyz') xyz sage: xyz ... NameError: name 'xyz' is not defined
comment:7 in reply to: ↑ 5 Changed 4 years ago by
I still have to find a case where injection of the variable itself matters.
Sorry, I should say... where injection via globals
in the var
function makes a difference versus ex = SR.var(...)
, where presumably globals
is filled by iPython with the ex
.
comment:8 follow-ups: ↓ 9 ↓ 13 Changed 4 years ago by
The name var
by itself makes a lot of confusion. There are Python variables which are a very different concept. What about calling it symbol
or Symbol
as it is done in sympy?
Vincent
comment:9 in reply to: ↑ 8 ; follow-up: ↓ 10 Changed 4 years ago by
Replying to vdelecroix:
The name
var
by itself makes a lot of confusion. There are Python variables which are a very different concept. What about calling itsymbol
orSymbol
as it is done in sympy?
+1
Eric.
comment:10 in reply to: ↑ 9 Changed 4 years ago by
Replying to egourgoulhon:
Replying to vdelecroix:
The name
var
by itself makes a lot of confusion. There are Python variables which are a very different concept. What about calling itsymbol
orSymbol
as it is done in sympy?+1
Eric.
+1 as well, the name "variable" causes a lot of troubles to newcommers that use var()
to declare Python names, see for example this list of ask questions. Instead of a generic "variable", we should use "symbol" for SR, "name" (for Python), "indeterminate" for polynomials. This will help learning Sage a lot.
By the way, note the difference between
sage: SR.var('x,y') (x, y)
and
sage: SR.symbol('x,y') x,y sage: SR.symbol('x y') x y
So perhaps should we also make a difference between Symbol
and Symbols
?
comment:11 follow-ups: ↓ 12 ↓ 17 Changed 4 years ago by
SR.var
does exactly what the declare_var
does without the drawbacks of an additional global, so as it stands I'm against the ticket description.
SR.symbol
and SR.var
should probably be aliases but aren't. I wasn't even aware of SR.symbol
, which is why I only fixed the comma parsing in SR.var
in #7496.
How about something like R.<x> = SR()
to get symbolic generators?
comment:12 in reply to: ↑ 11 Changed 4 years ago by
Replying to vbraun:
How about something like
R.<x> = SR()
to get symbolic generators?
Yes, that's probably better that the syntax I suggested.
comment:13 in reply to: ↑ 8 ; follow-up: ↓ 14 Changed 4 years ago by
Replying to vdelecroix:
The name
var
by itself makes a lot of confusion. There are Python variables which are a very different concept. What about calling itsymbol
orSymbol
as it is done in sympy?
Definitely not Symbol()
. As for symbol()
, it could have been a better name choice than var()
, but I doubt switching to it now is a good idea. The benefits are not that significant, and since var()
is proabably one of the most widely used functions outside the sage tree itself (in particular, in random code snippets), the compatibility break would be a pain for many of people (the recent deprecation of pol.coeffs()
already was pretty bad from this point of view).
comment:14 in reply to: ↑ 13 ; follow-ups: ↓ 15 ↓ 16 Changed 4 years ago by
Replying to vbraun:
SR.symbol
andSR.var
should probably be aliases but aren't. I wasn't even aware ofSR.symbol
, which is why I only fixed the comma parsing inSR.var
in #7496.
Actually SR.var
is a wrapper over SR.symbol
that splits commas and spaces to create tuples of symbols.
How about something like
R.<x> = SR()
to get symbolic generators?
This is non-pythonic, requires additional preparsing, needs to create the name R
while the ring SR
is already here, and will add even more confusion to newcomers that hardly understand the difference between a symbolic expression like x^2+1
and a well defined polynomial over a well defined ring.
Replying to mmezzarobba:
Definitely not
Symbol()
. As forsymbol()
, it could have been a better name choice thanvar()
, but I doubt switching to it now is a good idea. The benefits are not that significant, and sincevar()
is proabably one of the most widely used functions outside the sage tree itself (in particular, in random code snippets), the compatibility break would be a pain for many of people (the recent deprecation ofpol.coeffs()
already was pretty bad from this point of view).
The benefits are very significant for newcomers and anyone that interacts with newcomers, i personally spent a huge amount of time to deal with that issue (on ask.sagemath.org (see my previous link for a small sample) but also during tutorials).
Sage is full of inconsistencies, refusing to clean them because they are used increases the entry cost and will eventually lead to an obscure language where each function/method has its own semantics. This is not long-term viable. This is why we have a deprecation policy. For such a function, we could make the deprecation message more verbose and pedagogical than usual.
comment:15 in reply to: ↑ 14 Changed 4 years ago by
Thierry, I agree fully but:
How about something like
R.<x> = SR()
to get symbolic generators?This is non-pythonic, requires additional preparsing, needs to create the name
R
while the ringSR
is already here, and will add even more confusion to newcomers that hardly understand the difference between a symbolic expression likex^2+1
and a well defined polynomial over a well defined ring.
But it would be consistent and allow different ring types (later). Actually declare_var
could be provided additionally.
comment:16 in reply to: ↑ 14 ; follow-up: ↓ 18 Changed 4 years ago by
Replying to tmonteil:
This is non-pythonic, requires additional preparsing, needs to create the name
R
while the ringSR
is already here, and will add even more confusion to newcomers that hardly understand the difference between a symbolic expression likex^2+1
and a well defined polynomial over a well defined ring.
I don't care much about "pythonicity", but what Volker suggests would be consistent with the rest of Sage. And since this is all for interactive use anyway, I don't see the problem with using the preparser, nor with writing _.<x> = SR()
.
From a pedagogical point of view, it might actually be a good thing to make it clearer that var()
(or, to be precise, symbol()
) is more or less the same as gen()
, only for SR
.
Sage is full of inconsistencies, refusing to clean them because they are used increases the entry cost and will eventually lead to an obscure language where each function/method has its own semantics. This is not long-term viable.
I tend to agree in general, but I am not convinced in this particular case. Having a longer function name is inconvenient, and I find symbol()
only marginally clearer (if at all) than SR.var()
. (I'm fine with either removing var()
entirely or making it equivalent to SR.var()
, however.)
This is why we have a deprecation policy.
The deprecation policy is a joke... Except perhaps for a few _
-functions, just about anything in sage can be considered public, but only few changes are considered worth a deprecation.
For such a function, we could make the deprecation message more verbose and pedagogical than usual.
Yes, but please keep in mind that it will pop up everywhere for a long time.
comment:17 in reply to: ↑ 11 Changed 4 years ago by
Replying to vbraun:
SR.var
does exactly what thedeclare_var
No, it does not:
SR.var
a symbol and does not inject anything.var
(toplevel) as it exists now returns a symbol and injects a binding to itdeclare_var
as proposed would inject a binding to a symbol and returnNone
.
The problem with the mixed actions of var
is that people learn it, see it returns a symbol, and then use it in circumstances where they need a symbol returned. That works, but behind the scenes there is also a binding injected. That then surprises them later.
If we have two routines, one that only returns a symbol and the other that only injects a binding, this potential for surprise is eliminated.
I think we do want the possibility of injecting something, because x=var('x')
or x=SR.var('x')
is too verbose (and more importantly, requires the x
to by typed twice).
We need the injection capability on toplevel because this is one of the first things that novices need to be able to do. I'm not completely sure we need an entry to SR.var
in the global namespace. However, the capability has been there as part of var
(with sometimes surprising side-effects) so I think we pretty much have to continue it.
SR.symbol
andSR.var
should probably be aliases but aren't. I wasn't even aware ofSR.symbol
, which is why I only fixed the comma parsing inSR.var
in #7496.
They probably shouldn't. At least one of them should be 'make a symbol with the given print name or raise an error'. The fact that the return type of var
depends on the formatting of the string (either a symbol or a tuple of symbols) is a wart that stems from convenience for the injection purpose.
How about something like
R.<x> = SR()
to get symbolic generators?
Cute, but I think it misses the mark for the intended audience: complete novices. It makes it very hard to convince people that Sage is a reasonable choice relative to Maple and Mathematica (and Maxima), where you can just start using a symbol.
comment:18 in reply to: ↑ 16 ; follow-up: ↓ 19 Changed 4 years ago by
Replying to mmezzarobba:
I don't care much about "pythonicity", but what Volker suggests would be consistent with the rest of Sage. And since this is all for interactive use anyway, I don't see the problem with using the preparser, nor with writing
_.<x> = SR()
.From a pedagogical point of view, it might actually be a good thing to make it clearer that
var()
(or, to be precise,symbol()
) is more or less the same asgen()
, only forSR
.
It is not consistent with the rest of sage and hard to implement, since presently it amounts to
_ = SR(names=('x',)); (x,) = _._first_ngens(1)
Normally, calling a constructor with different names gives different results:
sage: PolynomialRing(QQ,names=('x',)) == PolynomialRing(QQ,names=('y',)) False
and we would need to hack SR._first_ngens
to remember the last set of generators that got returned.
The scenario really doesn't fit in the current meaning of _.<..>=...
, neither in implementation nor in semantics.
comment:19 in reply to: ↑ 18 Changed 4 years ago by
Replying to nbruin:
It is not consistent with the rest of sage and hard to implement, since presently it amounts to
_ = SR(names=('x',)); (x,) = _._first_ngens(1)
Normally, calling a constructor with different names gives different results:sage: PolynomialRing(QQ,names=('x',)) == PolynomialRing(QQ,names=('y',)) Falseand we would need to hack
SR._first_ngens
to remember the last set of generators that got returned.
I mean from a UI point of view. Otherwise, sure, it would require the preparser to repeat the names when asking for the generators, or something similar.
Is there another variant that you like better?
comment:20 follow-up: ↓ 21 Changed 4 years ago by
Remember that defining a new mathematical variable might be the first thing that a new Sage user will want to do, so from a UI point of view, _.<x> = SR()
is a disaster. It looks like a meaningless string of symbols. var('x')
or declare_var('x')
or symbol('x')
or similar at least have a chance to indicate some meaning when someone glances at the code/worksheet/notebook.
Maybe something like math_variable('x')
would convey what Sage is doing here, and in particular will distinguish this from Python variables.
comment:21 in reply to: ↑ 20 Changed 4 years ago by
Replying to jhpalmieri:
Remember that defining a new mathematical variable might be the first thing that a new Sage user will want to do, so from a UI point of view,
_.<x> = SR()
is a disaster. It looks like a meaningless string of symbols.var('x')
ordeclare_var('x')
orsymbol('x')
or similar at least have a chance to indicate some meaning when someone glances at the code/worksheet/notebook.
Well, then, form that point of view, I find x = SR.var('x')
much better. It clarifies in particular (i) that you are assigning an object to a certain Python variable, and (ii) that the indeterminate you are creating belongs to a particular parent--often not the one you want if you are using sage in the first place!
comment:22 follow-up: ↓ 23 Changed 4 years ago by
Re SR.var('x')
: "What does SR mean?" "The Symbolic Ring." "What's a ring?"
Remember that we have users who just want to do calculus. They don't know what a ring is. They also are not that familiar with Python, and we shouldn't use this particular situation to educate them on Python syntax. So I think we need a top-level function. The proposed declare_var('x')
, which returns None
but injects the variable into the global namespace, seems like the more natural behavior for novices. (We can have two functions, as Nils says, one like this and a second one which does not inject anything but returns the symbol. I would suggest advertising the first of these in the tutorial and other parts of the documentation. )
The name declare_var
could maybe be improved because of the different uses of the word "variable". Something like declare_math_var
? declare_math_symbol
? new_math_symbol
?
comment:23 in reply to: ↑ 22 ; follow-up: ↓ 24 Changed 4 years ago by
Replying to jhpalmieri:
Re
SR.var('x')
: "What does SR mean?" "The Symbolic Ring." "What's a ring?"Remember that we have users who just want to do calculus. They don't know what a ring is. They also are not that familiar with Python, and we shouldn't use this particular situation to educate them on Python syntax.
I doubt you can use sage (and not shoot yourself in the foot on every possible occasion) without understanding this kind of things at least a little. And for sure I've seen intelligent people with a very reasonable level in math, use sage in teaching while completely misunderstanding how basic things work... because, at first, they just wanted to do calculus, so they were led to use things like var('x')
without understanding what they did, and basically assumed that names in sage had the same kinds of semantics as in maple.
The name
declare_var
could maybe be improved because of the different uses of the word "variable". Something likedeclare_math_var
?declare_math_symbol
?new_math_symbol
?
declare_symbolic_variable
perhaps, if you really feel such a function is useful?
comment:24 in reply to: ↑ 23 ; follow-up: ↓ 25 Changed 4 years ago by
Replying to mmezzarobba:
I doubt you can use sage (and not shoot yourself in the foot on every possible occasion) without understanding this kind of things at least a little.
You can do some very simple examples, such as differentiating a function, plotting one, trying to compute an antiderivative without understanding the way python names (really, python has "names" in its namespaces. Variables have other connotations) and SR symbols interact; sort of the level of "wolfram alpha". We have to give people at that level at least a way into sage, otherwise they don't even get to shoot themselves in the foot, experience that as unpleasant and then gain the motivation to learn how to avoid that in the future.
There's a reason why maple, mathematica, maxima went with their approach. We can't quite do that, but we have to make the hurdle as low as possible. I think
var('x')
or
declare_var('x')
are about the best we can do. I think it's a problem they return something in addition to injecting a binding. If we need to produce feedback on the action taken, I think printing something would be preferable (it's a routine that's only meant to be used interactively anyway), so perhaps:
sage: declare_var('x,y') Declaring x, y as symbolic variables sage: A=declare_var('z') Declaring z as a symbolic variable sage: A None sage: declare_var('w',quiet=True) sage:
(where the quiet
would be the gateway to getting people to use other ways--perhaps we shouldn't provide that)
I'd be completely OK with declare_var
being spelled as var
too. The main thing is that I think it has been shown that injecting as well as returning something is harmful, so I hope we can our change our interface to not do that.
comment:25 in reply to: ↑ 24 Changed 4 years ago by
comment:26 follow-up: ↓ 27 Changed 4 years ago by
declare_var
sounds like varibale declaration that exists in many languages and is completely different that what is done here. There is already a lot of newcomer's code that start with
var('a') a=2
So i can imagine how much more there will be with declare_var
, since it will add confusion to programmers too.
What is currently discussed is a function that injects a symbol into the namespace, so inject_symbol
sounds more meaningful as it just tells what is actually done.
As for the Symbolic Ring
, it is perhaps better not to know what a mathematical ring is, so perhaps thinking about that object as The Lord of the Rings
is not so bad. We do not have much "calculus" course in France so i can not tell much about the benefits of playing with objects we are not able to define, i can just witness from what i see on ask.sagemath.org that people get more lost by lack of clear definitions than by excess (e.g. how to factor a polynomial if we do not know on which ring it is defined?).
I agree that in any case, both injecting and returning is harmful (function
does that too).
comment:27 in reply to: ↑ 26 ; follow-up: ↓ 29 Changed 4 years ago by
Replying to tmonteil:
What is currently discussed is a function that injects a symbol into the namespace, so
inject_symbol
sounds more meaningful as it just tells what is actually done.
Except that "inject" is rather technical and not what the novice thinks about doing. You'd probably have to explain to them: "Before using y, you have to declare that it is a math symbol, which you do by
declare_symbol("y")
."
I indeed agree that using "symbol" instead of "symbolic variable" would be better to distinguish the concept from a python name. However, we've been calling these things "var" since the start of sage, so changing that may be painful for our current users.
I agree that in any case, both injecting and returning is harmful (
function
does that too).
There the terminology is even worse: python actually calls its "def" and "lambda" objects "functions". So "declare_function" will be even more confusing than "declare_var".
A minimal plan is to either not have var
produce side-effects, meaning
sage: var('z') z sage: z+1 Error
or let var
return None, meaning
sage: z = var('z') sage: z+1 Error
If neither one is palatable then we can stop the discussion now. We're stuck with a bad design decision, for which the pain for repairing it is too high.
An alternative is to migrate to the unspoilt
sage: z = symbol('z') sage: declare_symbol('w') sage: z+w+1 z+w+1
and let var
rot and fester, trying to nudge people away from it (put a deprecation on it after a while?). In 10 years or so we could think of removing var
.
For function
we can perhaps do the deprecation in-place. That's mainly going to affect people trying to solve ODEs and PDEs (outside of that, symbolic (formal/abstract) functions don't have much use)
comment:28 Changed 4 years ago by
- Cc kcrisman added
comment:29 in reply to: ↑ 27 ; follow-up: ↓ 30 Changed 4 years ago by
Replying to nbruin:
An alternative is to migrate to the unspoilt
sage: z = symbol('z') sage: declare_symbol('w') sage: z+w+1 z+w+1and let
var
rot and fester, trying to nudge people away from it (put a deprecation on it after a while?). In 10 years or so we could think of removingvar
.
IMHO, this should be the good strategy. declare_symbol
is a bit long however and could be replaced by simply symbol
, while the above symbol
could be replaced by something like SR.get_symbol
. Indeed, the end user has hardly the need of z = symbol('z')
(am I right ?), so replacing it by something "sophisticated" like z = SR.get_symbol('z')
seems fine. To summarize, the above code would become
sage: z = SR.get_symbol('z') sage: symbol('w') # what end users really need sage: z+w+1 z+w+1
comment:30 in reply to: ↑ 29 ; follow-up: ↓ 32 Changed 4 years ago by
Replying to egourgoulhon:
Indeed, the end user has hardly the need of
z = symbol('z')
(am I right ?),
Searching our current codebase and examples indicates you might not be right in that assumption. I didn't do a precise count, but the occurrences of a=var('a')
and c,d=var('c,d')
are quite frequent (half of the var
occurrences maybe?), so there's definitely an immediate need for it to do an automatic replacement.
The fact that this developed in the first place also suggests that a significant number of people were not aware of/did not trust the injecting behaviour of var
, so it's probably "surprising" behaviour (it's certainly non-pythonic to go and scribble in the globals dictionary). That indicates to me that the routine doing it needs a name that makes explicit it's having a side effect. A normal way of doing that is making the name a verb or verbal phrase, hence declare_var
or declare_symbol
.
For obtaining a symbol as a return value a noun or nominal phrase should be OK, hence var
or symbol
.
comment:31 Changed 4 years ago by
- Why not
SR.gen('x')
to create a new variable instead ofSR.symbol
/SR.var
? Also: currently broken, so would be nice to fix.
- To inject variables into the namespace, why not attach a method
ex.inject_variables()
to symbolic expressions that injects the output ofex.variables()
.
At the end of the day, I don't think that the purity of functional programming conventions is worth the pain of changing how var
behaves. Yes it does chafe against your OCD. But imagine you don't know Python and just want to do some symbolic stuff. I can guarantee you that that user is not going to appreciate your lesson in functional programming.
comment:32 in reply to: ↑ 30 ; follow-up: ↓ 33 Changed 4 years ago by
Searching our current codebase and examples indicates you might not be right in that assumption. I didn't do a precise count, but the occurrences of
a=var('a')
andc,d=var('c,d')
are quite frequent (half of thevar
occurrences maybe?), so there's definitely an immediate need for it to do an automatic replacement.The fact that this developed in the first place also suggests that a significant number of people were not aware of/did not trust the injecting behaviour of
var
, so it's probably "surprising" behaviour (it's certainly non-pythonic to go and scribble in the globals dictionary). That indicates to me that the routine doing it needs a name that makes explicit it's having a side effect. A normal way of doing that is making the name a verb or verbal phrase, hencedeclare_var
ordeclare_symbol
.
Well, of course another reason is that in doctests it can be annoying to do
sage: var('a') a
while
sage: a=var('a') sage:
seems cleaner. Maybe it's not all due to confusion.
comment:33 in reply to: ↑ 32 ; follow-up: ↓ 34 Changed 4 years ago by
Replying to kcrisman:
Well, of course another reason is that in doctests it can be annoying to do
sage: var('a') awhile
sage: a=var('a') sage:seems cleaner. Maybe it's not all due to confusion.
I'm not quite clear whether you mean:
- side-effectful routines should NOT return a value (quite standard) and should NOT print something.
- side-effectful routines are unclean anyway and it's no bother to type
a=var('a')
.
comment:34 in reply to: ↑ 33 ; follow-up: ↓ 35 Changed 4 years ago by
seems cleaner. Maybe it's not all due to confusion.
I'm not quite clear whether you mean:
- side-effectful routines should NOT return a value (quite standard) and should NOT print something.
- side-effectful routines are unclean anyway and it's no bother to type
a=var('a')
.
What I mean is that I think the doctests were written that way because it was easier to typea=var('a')
than have to deal with an output. For myself, I think that
- keeping previous behavior
- ease of use
argue strongly for var('a')
or something else easy, no equals signs etc. In fact, var(a)
would be easiest but Python wouldn't allow that.
comment:35 in reply to: ↑ 34 Changed 4 years ago by
Replying to kcrisman:
...
var(a)
would be easiest but Python wouldn't allow that.
We're not so picky when it comes to other preparsed stuff.
comment:36 follow-ups: ↓ 37 ↓ 51 Changed 4 years ago by
How about
sage: var a, b
handled by the preparser. Its not a function, so it alleviates the side effect concern. The var
statement would only be allowed at the beginning of the line (just like the print statement), so you wouldn't be able to assign anything anyways. And its easily handled by the preparser as '^[\s]*var '
regex.
comment:37 in reply to: ↑ 36 Changed 4 years ago by
Replying to vbraun:
How about
sage: var a, b
Note that SageMathCloud supports
sage: %var a, b
for this.
comment:38 in reply to: ↑ description Changed 4 years ago by
Replying to rws:
var('x')
prints deprecation message, returns variable as before; error after deprecation periody = var('x')
as before (but without globals), NO deprecation message
I think it's technically impossible that var('x')
and y = var('x')
behave in a different way.
comment:39 follow-up: ↓ 41 Changed 4 years ago by
Then what about doing the following:
- Implement
%var a, b
(or perhapsvar a, b
) and/orSR.var('a').inject()
, leavingvar()
alone for the moment. - Change as much as possible of the documentation and examples to use either
a = SR.var('a')
or%var a
. - Wait a year or two to see if the new syntax catches on.
- Formally deprecate
var()
, add a deprecation warning, a remove it after a while.
comment:40 Changed 4 years ago by
Please change the description accordingly if no one has serious arguments (I think it's optimal). If asked about %var
/var
my purely personal preference would be the latter.
comment:41 in reply to: ↑ 39 ; follow-up: ↓ 42 Changed 4 years ago by
Replying to mmezzarobba:
- Implement
%var a, b
(or perhapsvar a, b
) and/orSR.var('a').inject()
, leavingvar()
alone for the moment.- Change as much as possible of the documentation and examples to use either
a = SR.var('a')
or%var a
.
You'd need to take into account that the %var
processing is done by the REPL. So for notebook, ipython and the doctest framework (and any new interfaces that arise) you'd have to provide it.
By making var
not a function you're also blocking off reasonable programmatic use. Presently:
sage: var(''.join('x%d '%i for i in [1..10])) (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)
This is comparable to why in Python3 print was turned into a function.
Otherwise I like the missing quotes in the syntax; I dislike having to explain what the modulo or string formatting sign is doing at the start of a line when you're explaining to someone that sage is "just like python" (should they know that already).
SR.var('a').inject()
has problems. The incantation is obviously atrocious to type. But also: note that inject()
would simply be a method on an expression. Would it inject all variables that occur in it?
comment:42 in reply to: ↑ 41 ; follow-up: ↓ 46 Changed 4 years ago by
Replying to nbruin:
By making
var
not a function you're also blocking off reasonable programmatic use.
Exactly, and that is IMHO a big plus of the proposal.
Even right row you are not supposed to use var
in library code, but there is nothing stopping you. Programmatically generated symbolic variables should always be declared as x = SR.var('x')
. This is already spelled out in the current var
docstring.
comment:43 Changed 4 years ago by
PS: The Sage doctests are preparsed but not run in IPython. So var a
would work in a doctest, whereas %var a
would not.
comment:44 Changed 4 years ago by
- Description modified (diff)
comment:45 Changed 4 years ago by
NOTE: I am making changes to the implementation of var()
in #18083.
comment:46 in reply to: ↑ 42 Changed 4 years ago by
Replying to vbraun:
Even right row you are not supposed to use
var
in library code, but there is nothing stopping you. Programmatically generated symbolic variables should always be declared asx = SR.var('x')
. This is already spelled out in the currentvar
docstring.
Is that spelled out? I looked at sage.calculus.var.var? (that's the var that also occurs at top-level) and didn't find it there. I don't think a programmatic approach to injecting x1,...,x10
is so bad.
A possible scenario:
sage: A=var(''.join('x%d '%i for i in [1..10])) sage: L=sum(A[i]^(i+1) for i in [0..9])^2 sage: L.expand().coefficient(x4^4) 2*x10^10 + 2*x9^9 + 2*x8^8 + 2*x7^7 + 2*x6^6 + 2*x5^5 + 2*x3^3 + 2*x2^2 + 2*x1
which actually illustrates a genuine use of "inject as well as return". D'oh.
comment:47 follow-up: ↓ 49 Changed 4 years ago by
The var docstring (var?
) contains
Note: The new variable is both returned and automatically injected into the global namespace. If you need symbolic variable in library code, it is better to use either SR.var() or SR.symbol().
comment:48 Changed 4 years ago by
See also #18084.
comment:49 in reply to: ↑ 47 Changed 4 years ago by
Replying to vbraun:
The var docstring (
var?
) containsNote: The new variable is both returned and automatically injected into the global namespace. If you need symbolic variable in library code, it is better to use either SR.var() or SR.symbol().
Yes that's for use in the library. The problem with %var
would be feeding programmatically generated input into it. I don't think that's currently explicitly discouraged in the documentation.
Reducing support for that would be a reduction in functionality. If the advantages of %var
are otherwise overwhelming we could still decide to go that route, but it shows that the spelling with quotes does have its advantages too.
comment:50 follow-up: ↓ 54 Changed 4 years ago by
Oh you mean the good old var(', '.join(['x{0}'.format(i) for i in range(10)]))
trick. Calculus freshmen are going to love your class... In any case I don't mind having a way to inject multiple variables at once, but something like
sage: SR.inject_variables('x, y') sage: SR.inject_variables('x', 'y') # strings to variable names sage: SR.inject_variables('x{i}', i=range(10)) # use keyword arguments to format sage: SR.inject_variables(ex) # inject all of ex.variables()
would probably be a lot better.
comment:51 in reply to: ↑ 36 Changed 4 years ago by
How about
sage: var a, b
Something along these lines would MASSIVELY help with this issue. As long as we have to declare variables anyway, we should at least make it easy to do so, and the syntax currently is kind of hard to type
var('z,y')
(try this on a qwerty board slowly to see all the unusual movements due to the shifts and non-home row things) so it would definitely be so for a beginner.
As long as there is a LONG deprecation period for this (as it's likely to bite quite a few people who wouldn't upgrade very frequently) something along these lines seems fine, return value None
or whatever seems appropriate. It would also be great to have %var
eventually since SMC does but that could be a different ticket as long as there is a good deprecation period to var('a')
.
comment:52 Changed 4 years ago by
I would favor an "infinite" deprecation period for var()
: deprecate it but keep supporting it forever.
comment:53 Changed 4 years ago by
Seems reasonable.
comment:54 in reply to: ↑ 50 Changed 4 years ago by
Replying to vbraun:
sage: SR.inject_variables('x, y') sage: SR.inject_variables('x', 'y') # strings to variable names sage: SR.inject_variables('x{i}', i=range(10)) # use keyword arguments to format sage: SR.inject_variables(ex) # inject all of ex.variables()
Something like that would work, but probably not under that name. The method already exists on SR
by inheritance (and doesn't work), and the signature you're proposing is incompatible with the one on other rings.
Injecting in general isn't really a method that belongs on the object, since the object doesn't naturally have access to the dictionary into which these things should be injected. It's really more the task of a REPL utility function. In which case the spelling
inject_generators(QQ['x'])
would make more sense. The magic of figuring out into which dictionary the bindings should be injected is compartmentalized into a single function which could be implemented basically as
def inject_generators(parent): D={repr(a): a for a in parent.gens()} print "defining ",D.keys() user_globals.update(D)
This probably much nicer than scattering references to user_globals all over the place (we'd probably want to add some sanity checks to prevent this from inserting objectionable bindings).
For symbolic binding we could then have something along the lines of
def inject_symbols(*args): user_globals.update({ repr(a):a for e in args for a in e.variables()})
Interfacing via %var
is then an additional measure.
In short, what we seem to be converging towards is:
- deprecate
sage.calculus.var.var
(but keep supporting it; after a while probably do adorn it with a deprecation warning) - support symbolic variable injection via a special
%var
directive (which saves quotes too!) -- Is the%
a problem? We'd need to make doctests aware of it. - have
SR.var(...)
as general symbol creation (which we already have).
There is some further rationalization around injection behaviour possible.
comment:55 Changed 4 years ago by
I've announce this discussion on sage-devel https://groups.google.com/forum/#!topic/sage-devel/iy8Ck6BbhSE
comment:56 follow-up: ↓ 58 Changed 4 years ago by
I'm against deprecating var.
It is also common for a Python function to do something with side effects -- e.g., run a subprocess -- and also return some information about what it did, e.g., the exit code. This is computer programming, not mathematics.
I prefer
%var x, y
to
var x, y
by the way, since we have been generally deprecated non-% special commands. I was annoyed at first by, e.g., Jason Grout doing this, but I've come around.
comment:57 Changed 4 years ago by
os.system is a terrible example, its just a syscall wrapper. The subprocess module precisely tries to improve that interface by giving you separate check_output / check_call functions so you can create subprocesses in a more pythonic manner.
comment:58 in reply to: ↑ 56 Changed 4 years ago by
Replying to was:
It is also common for a Python function to do something with side effects -- e.g., run a subprocess -- and also return some information about what it did, e.g., the exit code. This is computer programming, not mathematics.
I thought something similar originally as well (although I thought "this is mathematics software, not computer programming"), but after seeing several questions from people getting thoroughly confused, I came to the conclusion that in this case having a side effect and a return value is a major source of confusion. See the original comment
http://trac.sagemath.org/ticket/17447#comment:23. This is exacerbated by the fact that x=var('x')
is very common in the documentation, which further trains people to be unaware of the side effect of var
. See comment:32 for a hypothesis on why this happens (which argues why the return value of the side-effect-having var
is a nuisance rather than helpful)
We really need to decide if deprecating the current behaviour of var
is ever going to be doable (possibly with supporting indefinitely). If it's not we can stop right now: we'll just be adding extra interfaces which will only confuse people more. In that case it'll just be another victim of the tar-pits of interface compatibility (which does have value).
comment:59 Changed 4 years ago by
- Many examples given so far use
sage: var('x')
whilex
is the one variable that is injected automatically into the namespace. I feel like this particularity ofx
does not help an easy understanding of how it works for newcomers. - Many users may be satisfied if any non-yet-defined symbol was automatically injected into the namespace. I guess it would also help users to understand the difference between a "symbol" (from
SR
), on which nothing is known, and a "polynomial variable" (for instance fromZZ['x']
). In some sense, this would imply that one can play around with symbolic variables, perform some simple calculations, etc. but that one should properly define their objects to obtain more functionalities and better performances. - Note that the previous point can be activated, in the Notebook only, using
automatic_names(True)
. We may have a magic function to activate this behavior, as well asimplicit_multiplication(True)
. - Amongst the different propositions for a new name if one is needed, I like the use of the keyword
math
that helps to makes the difference between a math symbol/variable/whatever and a python variable. - I also like the proposition to define
%var x,y
(or even%var x y
). I think it would be nice to have something printed as forinject_variables()
in this case, such asDefining x, y as symbolic variables.
- For the deprecation, it is certainly less annoying for users if the change occurs with a new version number such as 7.0 or 8.0.
comment:60 follow-up: ↓ 61 Changed 4 years ago by
I won't approve of declare_var
, as it's too close to declare_war
...
Anyhow, I don't understand what SR
is. It looks as if it is no more than a hack provided by Sage, no more than that. Documentation says nothing about it. Perhaps before discussing var()
, one should provide a definition. So far I don't understand the difference between symbolic variables and polynomial ring variables (except that the latter somehow don't work in solve()
).
comment:61 in reply to: ↑ 60 ; follow-up: ↓ 65 Changed 4 years ago by
Replying to dimpase:
Anyhow, I don't understand what
SR
is. It looks as if it is no more than a hack provided by Sage, no more than that. Documentation says nothing about it. Perhaps before discussingvar()
, one should provide a definition. So far I don't understand the difference between symbolic variables and polynomial ring variables (except that the latter somehow don't work insolve()
).
Calculus.
comment:62 follow-up: ↓ 63 Changed 4 years ago by
To get this back on track, the minimal change that would satisfactorily resolve the issue would be a warning (but keep var
indefinitely)
sage: var('x, y') # warning but keep indefinitely Warning: var has side effects. Consider using %var x, y (x, y)
and
sage: %var x, y Defining x, y as symbolic variables.
The latter would also have to work in doctests where percent-magics currently do not work.
comment:63 in reply to: ↑ 62 ; follow-up: ↓ 64 Changed 4 years ago by
Replying to vbraun:
To get this back on track, the minimal change that would satisfactorily resolve the issue would be a warning (but keep
var
indefinitely)sage: var('x, y') # warning but keep indefinitely Warning: var has side effects. Consider using %var x, y (x, y)and
sage: %var x, y Defining x, y as symbolic variables.
I think this would be an improvement, so I'd be in favour if this change, even as proposed. Some details:
- the warning correctly mentions a snag about
var
and then proposes an alternative that also has side-effects. I hate to make warning messages more than one line, but perhaps more information is beneficial here:Warning: var has side effects. Consider using SR.var('x,y') to return symbolic variables and %var x,y for binding them.
- I personally like the printing of a message by
%var
for novices, but comment:32 suggests that the printing is a nuisance. So is it better for%var
to do its work silently?
comment:64 in reply to: ↑ 63 ; follow-up: ↓ 67 Changed 4 years ago by
Replying to nbruin:
- I personally like the printing of a message by
%var
for novices, but comment:32 suggests that the printing is a nuisance. So is it better for%var
to do its work silently?
An option may be to print it using verbose(level=0)
, and document that the verbosity level may be set to negative to suppress such messages.
comment:65 in reply to: ↑ 61 ; follow-up: ↓ 66 Changed 4 years ago by
Replying to was:
Replying to dimpase:
Anyhow, I don't understand what
SR
is. It looks as if it is no more than a hack provided by Sage, no more than that. Documentation says nothing about it. Perhaps before discussingvar()
, one should provide a definition. So far I don't understand the difference between symbolic variables and polynomial ring variables (except that the latter somehow don't work insolve()
).Calculus.
I don't think it's precise enough; for myself I understand SR
as sequences of terms subject to certain rewriting rules, but neither what the term are, nor what the rewriting rules are, is not stated anywhere except in the source code and in examples...
comment:66 in reply to: ↑ 65 Changed 4 years ago by
Replying to dimpase:
for myself I understand
SR
as sequences of terms subject to certain rewriting rules, but neither what the term are, nor what the rewriting rules are, is not stated anywhere except in the source code and in examples...
To repeat what I said on #15605, I for one basically view symbolic expressions as straight-line programs that are just required to evaluate to what you'd expect when you assign values to free variables. I believe this may be more accurate than thinking in terms of rewriting rules, since, as far as I know, nothing in the Sage implementation of symbolic expression systematically applies “rewriting rules”.
Many operations on symbolic expressions, however, only make sense with stronger assumptions on the expressions. Typically, simplifications are supposed to transform these ”programs“ into ”equivalent“ ones, but of course whether two ”programs“ are equivalent depends on what the variables can represent.
In this context, the sensible thing to do IMO is to view all variables as complex by default, and require simplifications to be valid for arbitrary complex values of all variables (or more accurately, for a generic choice of complex values: for example, we probably do want x/x
to simplify to 1
). But of course this default does not cover all cases, for instance, expand()
also makes sense for expressions containing constants from a finite field.
comment:67 in reply to: ↑ 64 Changed 4 years ago by
Replying to mmezzarobba:
An option may be to print it using
verbose(level=0)
, and document that the verbosity level may be set to negative to suppress such messages.
I'm pretty sure that would be at least as annoying as dealing with values printed by a bare var
(at least for doctests).
comment:68 follow-up: ↓ 69 Changed 4 years ago by
Related to this discussion, note that there is another (strange) way to declare variables in Sage that currently works:
sage: ,var x a b c (x, a, b, c) sage: type(a) <type 'sage.symbolic.expression.Expression'>
Apparently, it is ipython that provides this. I learn about the existence of this when I recently read the wikipedia page of Sage:
x, a, b, c = var('x, a, b, c') # Note that IPython also supports a faster way to do this, by calling # this equivalent expression starting with a comma: # ,var x a b c
comment:69 in reply to: ↑ 68 Changed 4 years ago by
Replying to slabbe:
Apparently, it is ipython that provides this. I learn about the existence of this when I recently read the wikipedia page of Sage:
x, a, b, c = var('x, a, b, c') # Note that IPython also supports a faster way to do this, by calling # this equivalent expression starting with a comma: # ,var x a b c
Yuck. It's a good illustration of the general confusion caused by the current behaviour of var
. Reading the IPython documentation,
,var x a b c
is equivalent to
var("x","a","b","c")
which happens to do almost the same effect as the other var
command given, except that the second form consists of a value returning expression and the first form is a non-expression statement that does not return a value (and hence prints nothing in the REPL).
Does sage really need a version of
var()
that touches the global namespace? Other similar functions likepolygen()
and*.gen()
don't do it, except for a handful of constructors afterinject_on()
—which I doubt anyone uses. Andy = var('y')
is clearer and shorter thandeclare_var('y')
, though it would be nice ify = var()
(or perhaps<y> = var()
?) was preparsed toy = SR.var('y')
.