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

implement declare_var, deprecate (None)var #17958

Open
rwst opened this issue Mar 14, 2015 · 66 comments
Open

implement declare_var, deprecate (None)var #17958

rwst opened this issue Mar 14, 2015 · 66 comments

Comments

@rwst
Copy link
Contributor

rwst commented Mar 14, 2015

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:

  1. declare_var('x') == var('x') as before but returning None
  2. var('x') prints deprecation message, returns variable as before; error after deprecation period
  3. y = 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.

CC: @nbruin @kcrisman

Component: symbolics

Issue created by migration from https://trac.sagemath.org/ticket/17958

@rwst rwst added this to the sage-6.6 milestone Mar 14, 2015
@rwst rwst changed the title implement declare_var implement declare_var, deprecate (None)var Mar 15, 2015
@rwst

This comment has been minimized.

@mezzarobba
Copy link
Member

comment:3

Does sage really need a version of var() that touches the global namespace? Other similar functions like polygen() and *.gen() don't do it, except for a handful of constructors after inject_on()—which I doubt anyone uses. And y = var('y') is clearer and shorter than declare_var('y'), though it would be nice if y = var() (or perhaps <y> = var()?) was preparsed to y = SR.var('y').

@rwst
Copy link
Contributor Author

rwst commented Mar 16, 2015

comment:4

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...

@rwst
Copy link
Contributor Author

rwst commented Mar 17, 2015

comment:5

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.

@mezzarobba
Copy link
Member

comment:6

I think I don't understand what you mean:

sage: SR.var('xyz')
xyz
sage: xyz
...
NameError: name 'xyz' is not defined

@rwst
Copy link
Contributor Author

rwst commented Mar 18, 2015

comment:7

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.

@videlec
Copy link
Contributor

videlec commented Mar 26, 2015

comment:8

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

@egourgoulhon
Copy link
Member

comment:9

Replying to @videlec:

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?

+1

Eric.

@sagetrac-tmonteil
Copy link
Mannequin

sagetrac-tmonteil mannequin commented Mar 26, 2015

comment:10

Replying to @egourgoulhon:

Replying to @videlec:

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?

+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 ?

@vbraun
Copy link
Member

vbraun commented Mar 26, 2015

comment:11

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?

@mezzarobba
Copy link
Member

comment:12

Replying to @vbraun:

How about something like R.<x> = SR() to get symbolic generators?

Yes, that's probably better that the syntax I suggested.

@mezzarobba
Copy link
Member

comment:13

Replying to @videlec:

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?

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).

@sagetrac-tmonteil
Copy link
Mannequin

sagetrac-tmonteil mannequin commented Mar 26, 2015

comment:14

Replying to @vbraun:

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.

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 @mezzarobba:

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).

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.

@rwst
Copy link
Contributor Author

rwst commented Mar 26, 2015

comment:15

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 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.

But it would be consistent and allow different ring types (later). Actually declare_var could be provided additionally.

@mezzarobba
Copy link
Member

comment:16

Replying to @sagetrac-tmonteil:

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.

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.

@nbruin
Copy link
Contributor

nbruin commented Mar 26, 2015

comment:17

Replying to @vbraun:

SR.var does exactly what the declare_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 it
  • declare_var as proposed would inject a binding to a symbol and return None.

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 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.

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.

@nbruin
Copy link
Contributor

nbruin commented Mar 26, 2015

comment:18

Replying to @mezzarobba:

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.

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.

@mezzarobba
Copy link
Member

comment:19

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',))
False

and 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?

@jhpalmieri
Copy link
Member

comment:20

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.

@mezzarobba
Copy link
Member

comment:21

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') 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.

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!

@jhpalmieri
Copy link
Member

comment:22

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?

@mezzarobba
Copy link
Member

comment:23

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 like declare_math_var? declare_math_symbol? new_math_symbol?

declare_symbolic_variable perhaps, if you really feel such a function is useful?

@nbruin
Copy link
Contributor

nbruin commented Mar 26, 2015

comment:24

Replying to @mezzarobba:

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.

@sagetrac-tmonteil
Copy link
Mannequin

sagetrac-tmonteil mannequin commented Mar 27, 2015

comment:26

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).

@nbruin
Copy link
Contributor

nbruin commented Mar 27, 2015

comment:27

Replying to @sagetrac-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)

@nbruin
Copy link
Contributor

nbruin commented Mar 29, 2015

comment:41

Replying to @mezzarobba:

  1. Implement %var a, b (or perhaps var a, b) and/or SR.var('a').inject(), leaving var() alone for the moment.
  2. 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?

@vbraun
Copy link
Member

vbraun commented Mar 29, 2015

comment:42

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.

@vbraun
Copy link
Member

vbraun commented Mar 29, 2015

comment:43

PS: The Sage doctests are preparsed but not run in IPython. So var a would work in a doctest, whereas %var a would not.

@jdemeyer

This comment has been minimized.

@jdemeyer
Copy link
Contributor

comment:45

NOTE: I am making changes to the implementation of var() in #18083.

@nbruin
Copy link
Contributor

nbruin commented Mar 29, 2015

comment:46

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 as x = SR.var('x'). This is already spelled out in the current var 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.

@vbraun
Copy link
Member

vbraun commented Mar 29, 2015

comment:47

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().

@jdemeyer
Copy link
Contributor

comment:48

See also #18084.

@nbruin
Copy link
Contributor

nbruin commented Mar 29, 2015

comment:49

Replying to @vbraun:

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().

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.

@vbraun
Copy link
Member

vbraun commented Mar 29, 2015

comment:50

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.

@kcrisman
Copy link
Member

comment:51

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').

@jdemeyer
Copy link
Contributor

comment:52

I would favor an "infinite" deprecation period for var(): deprecate it but keep supporting it forever.

@kcrisman
Copy link
Member

comment:53

Seems reasonable.

@nbruin
Copy link
Contributor

nbruin commented Mar 31, 2015

comment:54

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.

@dkrenn
Copy link
Contributor

dkrenn commented Apr 13, 2015

comment:55

I've announce this discussion on sage-devel https://groups.google.com/forum/#!topic/sage-devel/iy8Ck6BbhSE

@williamstein
Copy link
Contributor

comment:56

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.

@vbraun
Copy link
Member

vbraun commented Apr 13, 2015

comment:57

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.

@nbruin
Copy link
Contributor

nbruin commented Apr 13, 2015

comment:58

Replying to @williamstein:

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
[#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).

@bgrenet
Copy link
Contributor

bgrenet commented Apr 13, 2015

comment:59
  • Many examples given so far use sage: var('x') while x is the one variable that is injected automatically into the namespace. I feel like this particularity of x 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 from ZZ['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 as implicit_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 for inject_variables() in this case, such as Defining 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.

@dimpase
Copy link
Member

dimpase commented Apr 14, 2015

comment:60

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()).

@williamstein
Copy link
Contributor

comment:61

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 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()).

Calculus.

@vbraun
Copy link
Member

vbraun commented Apr 14, 2015

comment:62

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.

@nbruin
Copy link
Contributor

nbruin commented Apr 14, 2015

comment:63

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?

@mezzarobba
Copy link
Member

comment:64

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.

@dimpase
Copy link
Member

dimpase commented Apr 14, 2015

comment:65

Replying to @williamstein:

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 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()).

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...

@mezzarobba
Copy link
Member

comment:66

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.

@nbruin
Copy link
Contributor

nbruin commented Apr 14, 2015

comment:67

Replying to @mezzarobba:

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).

@seblabbe
Copy link
Contributor

comment:68

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

@nbruin
Copy link
Contributor

nbruin commented Apr 14, 2015

comment:69

Replying to @seblabbe:

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).

@mkoeppe mkoeppe removed this from the sage-6.6 milestone Dec 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests