The difference is that %i is provably non-real, while zcan be non-real.
Erroring on sign(z) would treat "nothing known about realness" the same as "provably non-real". It also has a cost outside of sign itself. is() routes through sign and converts this error into false (meaning "provably no") - the same path that makes is(%i > 0)false today.
So, if sign(z) raised an error, then is(z > 0) and is(z < 0) would both change from today's answer unknown to false ("provably no"). That would imply that a variable declared complex always has a non-zero imaginary part or is zero, which would in turn collide with csign's answer on e.g. log(x), where complex means "possibly non-zero imaginary part".
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I agree the root problem is that sign has no way to say "not known to be real". But the error can't fill that role, because is has to treat two cases differently: %i and a declared-imaginary j are definitely not real, so is(%i > 0) => false is correct. z is only possibly not real, so is(z > 0) has to stay unknown. If the error becomes false, z is handled wrong; if it becomes unknown, is(%i > 0) breaks.
So why not give sign the missing answers instead: let it return imaginary or complex (as csign already does) when the declarations leave no better option. Then sign(z) => complex, sign(j) => imaginary, sign(j^2) => nz, and is maps imaginary to false and complex to unknown. asksign would still reject anything not known to be real.
sign and csign would still differ in the same way as before: csign(sqrt(x)) => complex for undeclared x, while sign keeps treating undeclared variables and operations on them as real (sign(sqrt(x)) => pz, unless a fact says that x < 0). Only declarations would make sign return a complex answer.
One concern: sign returning values outside the documented seven could break internal callers and external code that do a case analysis on the result. My hope would be that most callers just test for specific values like pos and fall through otherwise, so complex would act like pnz does today rather than cause errors - but that remains to be seen.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Re is(%i>0) is not false, but meaningless since %$i is not part of the ordered field R. It should return unknown, just as other meaningless statements do, e.g. is([1]>0) and is({}<[]) return unknown.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Re "So why not give sign the missing answers instead: let it return imaginary or complex (as csign already does) when the declarations leave no better option. Then sign(z) => complex, sign(j) => imaginary, sign(j^2) => nz, and is maps imaginary to false and complex to unknown. asksign would still reject anything not known to be real."
The raison d'être of csign is precisely that the return values of sign are a closed set. We can't "hope" that our users' code doesn't depend on this.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The error can of course break existing code too, though at least in a more visible way.
How about this: Internally, let the imaginary/complex values flow through the machinery (as in csign), and fire the error only if the final result is still imaginary or complex. That would allow sign(j^2) => nz instead of an error on a provably real quantity.
is catches the error and would map it to unknown instead of false as you suggested, giving is(z > 0) => unknown, is(j > 0) => unknown and is(j^2 <= 0) => true. sign(sqrt(x)) stays pz for undeclared x of unknown sign, which is where sign would still differ from csign.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The difference is that
%iis provably non-real, whilezcan be non-real.Erroring on
sign(z)would treat "nothing known about realness" the same as "provably non-real". It also has a cost outside ofsignitself.is()routes throughsignand converts this error intofalse(meaning "provably no") - the same path that makesis(%i > 0)falsetoday.So, if
sign(z)raised an error, thenis(z > 0)andis(z < 0)would both change from today's answerunknowntofalse("provably no"). That would imply that a variable declaredcomplexalways has a non-zero imaginary part or is zero, which would in turn collide withcsign's answer on e.g.log(x), wherecomplexmeans "possibly non-zero imaginary part".Yes,
zmight be real, but I disagree thatsignshould assume that it is real and thus returnpnz.The root problem is that
signhas no way to returnunknownother than signaling an error. Ifisdepends on the current behavior, that is a bug inis.cf. my comments in #4922.
I agree the root problem is that
signhas no way to say "not known to be real". But the error can't fill that role, becauseishas to treat two cases differently:%iand a declared-imaginaryjare definitely not real, sois(%i > 0) => falseis correct.zis only possibly not real, sois(z > 0)has to stayunknown. If the error becomesfalse,zis handled wrong; if it becomesunknown,is(%i > 0)breaks.So why not give
signthe missing answers instead: let it returnimaginaryorcomplex(ascsignalready does) when the declarations leave no better option. Thensign(z) => complex,sign(j) => imaginary,sign(j^2) => nz, andismapsimaginarytofalseandcomplextounknown.asksignwould still reject anything not known to be real.signandcsignwould still differ in the same way as before:csign(sqrt(x)) => complexfor undeclaredx, whilesignkeeps treating undeclared variables and operations on them as real (sign(sqrt(x)) => pz, unless a fact says thatx < 0). Only declarations would makesignreturn a complex answer.One concern:
signreturning values outside the documented seven could break internal callers and external code that do a case analysis on the result. My hope would be that most callers just test for specific values likeposand fall through otherwise, socomplexwould act likepnzdoes today rather than cause errors - but that remains to be seen.Re
is(%i>0)is not false, but meaningless since%$iis not part of the ordered field R. It should returnunknown, just as other meaningless statements do, e.g.is([1]>0)andis({}<[])returnunknown.Re "So why not give sign the missing answers instead: let it return imaginary or complex (as csign already does) when the declarations leave no better option. Then sign(z) => complex, sign(j) => imaginary, sign(j^2) => nz, and is maps imaginary to false and complex to unknown. asksign would still reject anything not known to be real."
The raison d'être of
csignis precisely that the return values ofsignare a closed set. We can't "hope" that our users' code doesn't depend on this.The error can of course break existing code too, though at least in a more visible way.
How about this: Internally, let the
imaginary/complexvalues flow through the machinery (as incsign), and fire the error only if the final result is stillimaginaryorcomplex. That would allowsign(j^2) => nzinstead of an error on a provably real quantity.iscatches the error and would map it tounknowninstead offalseas you suggested, givingis(z > 0) => unknown,is(j > 0) => unknownandis(j^2 <= 0) => true.sign(sqrt(x))stayspzfor undeclaredxof unknown sign, which is wheresignwould still differ fromcsign.