In the show-your-work spirit, here was my line of thought. First, I thought that maybe all that was missing was adding a .n method to degree, to get degree to pretend as if it were pi/180, but that didn't work like I was hoping. Then I realized that this was silly: pi already behaves the way I want it to, I just want to define a new pi-like constant with name "degree" and value "pi/180". So looking at how pi is implemented, it subclasses Constant and then takes the expression from that. So in a similar spirit:
from sage.symbolic.constants import Constant
class ConstantFromExpression(Constant):
def __init__(self, name, expr):
conversions = dict(maxima=repr(maxima.coerce(expr)))
Constant.__init__(self, name,conversions=conversions)
self._expr = expr
def __float__(self): return float(self._expr)
def __complex__(self): return complex(self._expr)
def _mpfr_(self, R): return R(self._expr)
def _real_double_(self, R): return R(self._expr)
def NamedExpression(name, expr):
return ConstantFromExpression(name,expr).expression()
which should allow you to do what you want:
sage: degree = NamedExpression('degree', pi/180)
sage: degree
degree
sage: float(degree)
0.017453292519943292
sage:
sage: sin(25*degree)
sin(25*degree)
sage: a = sin(25*degree)
sage: a.n()
0.422618261740699
sage: RealField(100)(a)
0.42261826174069943618697848965
sage:
sage: sin(90*degree)
sin(90*degree)
sage: simplify(sin(90*degree))
1
sage: sin(45*degree)
sin(45*degree)
sage: simplify(sin(45*degree))
1/2*sqrt(2)
There are lots of conversions and features I didn't implement, of course, but I had to do at least the maxima one to get simplify to work correctly. I don't know enough about the maxima coercion repr to trust it entirely -- should probably put some tests in, to make sure the result tests equal to the input. But for this trivial case, it seems to work okay.
Maybe a NamedExpression wrapper would be useful?