On Mon, Jan 15, 2018 at 3:38 PM, Henri Tuhola <[email protected]> wrote: > The idea about changing the precedence and meaning of ^ from > XOR to exponentiation is a bit provoking. I've been > programming so long that I often just forget that ^ > means exponentiation for most people. > > I did a quick search and it reveals that the current Python > symbol for exponentiation (**) comes from Fortran. It > used that symbol because the ^ did not exist yet. The ^ as > bitwise XOR was introduced by C language. Even the search > results pointed out that this is confusing for newcomers. > > If I did what you propose, everyone who has been programming > C would do the mix up from time to time. But these guys are > actually more capable of dealing with the problem, compared > to people who might enter programming through symbolic > algebra. Replacing the XOR ^ with POW ^ could make a lot of > sense even if I did not really consider it at first. > > > Another, slightly wilder idea that I've had is that what if > you actually made ordinary numbers written by users bignums, > and numbers such as 1.2 would be fractionals by default. > This could slow down some programs considerably but the way > I see it the dynamic language is usually an excellent > starting point and when you go further from that it might be > better to instead have tools to optimize the dynamically > typed programs into something else.
Making floats rational numbers has a ton of pitfalls. The reason floating point numbers work the way they do is that floats always "truncate" the result of any calculation to fit in the floating point format (8 bytes for a double precision float). With rationals, the numerator and denominator invariable end up increasing, sometimes massively. For instance, if you add n rational numbers together, the denominator can be at least as big as the product of the denominators of the original numbers. Guido has also talked about this here https://python-history.blogspot.com/search?q=rational Floats also have an advantage of being very fast, since they are supported directly in the hardware. One thing that would be interesting, though, would be the ability to recover the original floating point number as a user typed it. In Python there's no way to distinguish 0.1 and 0.10000000000000001, for instance. In SymPy, if you want to create a Float or Rational from a float, the best practice is to input it as a string, so that you don't lose any precision from Python's interpretation of the float literal. In fact, as an aside, Python's eager evaluation is both a boon and a bane for SymPy. On the one hand, it makes the language very easy to reason about. On the other, we run into issues like the gotcha that expressions like x**(1/2) create x**0.5, when the version with the rational number x**Rational(1, 2) is almost always much preferred. There's no way around this at the language level. We just have to tell users to be careful when doing integer/integer (things are even worse in Python 2 where 1/2 produces 0, but fortunately, this is fixed in Python 3 and more and more people are using it). > > > I did follow Richard's advice and trying to list and/or > identify the points of friction. > > To help this off I made myself a model of what a typical > operator overloading looks like in Python at this moment: > https://gist.github.com/cheery/228b6651fb6a460b91f26195fe58e397 > > To resolve a+b, Python first calls a.__add__(b) for the > left-side object. If it returns NotImplemented, then it > tries b.__radd__(a). If it still returns NotImplemented then > it fails. That's the basic idea, though it does get more complicated than that when you consider multiple inheritance. > > Operator overloading in Python looks better than I remember. > It's probably because many guides and memos fail to state > the important part of how Python specifically resolves the > call and they do not even include the 'isinstance' -part to > identify the other side on the expression. > > In my language I have implemented the '+' as a multimethod > and added another multimethod to coerce. I were happy to > that for a while but then I realised that it may make some > things worse because the multimethod resolution ends up > being more complex rather than simpler, and not that many > problems appear to come from the dispatch mechanism after > all. > > Another thing that seemed important but is maybe not that > crucial flaw is that Python's approach ends up dominating > with the left side of __add__. If the operation is found in > the __add__ then it won't be searched from the __radd__ and > this can cause conflicts between different libraries that > extend the arithmetic. That is a problem, especially for things like SymPy that want to gobble things up into their own objects (like sympy.Add). In SymPy we haven't yet properly figured out how to allow objects to define their behavior in an Add object (things are complicated by the fact that Add is an n-ary function). Aaron Meurer > > In my opinion the real problem is, and will likely always > be in getting multiple libraries interoperate. I've been > looking that statically typed languages seem to handle this > better than dynamically typed ones but in the end it turns > out to be the same ways by which you successfully cope with > the challenges. > > If you have M different kinds of things, then in worst case > you need M*M different implementations for arithmetic. In an > one project it's likely that you cope well, but in presence > of multiple systems it becomes harder. > > I think these ideas boil down to this: When you create new > behavior for arithmetic you're creating a new numerical > system that extends from the base types you have. This new > "number system" describes how the new values behave with the > existing values. > > Languages like Haskell seem acknowledge that people create > new numerical systems when they overload operators. The > numbers are implicitly wrapped with (fromInteger N), and > you are supposed to define those conversion functions from > numeric values when you define the + operation. Different > numerical systems defined this way do not interact with each > other in Haskell. > > I've been studying subtyping in order to optimize > dynamically typed programs directly from their source code > and get them to translate and run in C or GLSL -like > environments alongside the dynamic portions of the programs. > I think that this interacts with the way how you overload > the arithmetic. > > > The another, kind of a potential starting point I've been > thinking about is related to how numerical libraries deal > efficiently with computing. > > The kind of an obvious thing that a programming language > implementation could provide would be tight memory layouts, > or even in-memory relational tables where you can fill up > the numerical data. The tight memory layouts would appear to > matter the most for efficient computing. > > Numerical data itself seems to always be bundles of numbers. > You either got quaternions or matrix data with single or > double precision floating point. > > I've been thinking that actually when you have quaternions > or matrices, it looks like neither one of those actually > need to take precedence and the number bundle end up staying > as an array. Perhaps those bundles could be treated as sums > of terms*constants, where the terms -part is a parametric > type? > > > Lisp is often mentioned being a family of various languages > such as CLisp, scheme, racket. If you people provided specific > examples it would help but I will try to find out those > myself as well. > > maanantai 15. tammikuuta 2018 21.04.31 UTC+2 Aaron Meurer kirjoitti: >> >> You might look at multiple dispatch as an improved method of operator >> overloading, such as what is implemented in Julia. There are also >> potentially more advanced things which can be useful, such as pattern >> matching (Mathematica, Haskell). >> >> You have to consider the tradeoffs, however, with expresibility. The >> most general possible system is a full macro system, which lets you >> effectively define whatever syntax you want. But the downside to that >> is that you no longer have a fully consistent language. Someone >> reading the code for a library must first learn the syntax. To >> contrast something like Python, which does not have a macro system and >> has (relatively) limited operator overloading, someone who already >> knows the language from other uses can read the SymPy code and have a >> good idea of what things mean (it also helps that SymPy and Python use >> a fairly strict adherence to standard mathematical notation). >> >> To give an example of this, we sometimes wish that Python allowed >> changing the operator precedence. For instance, we can't use ^ for >> exponentiation, because even though Python allows overriding that >> operator, it is fixed at a precedence lower than +, so a + b ^ 2 >> without parentheses will give (a + b)^2 (^ is XOR). The downside is >> that new users to Python must learn to use ** instead of ^ for >> exponents. The upside is that anyone already familiar with Python can >> see any SymPy expression and know how it will be interpreted, because >> operator precedence is uniform across all Python code, even if it >> overloads the operators to mean different things. >> >> So I think there's a sweet spot. We have run into limitations of >> Python's operator overloading mechanism. I personally think that >> multiple dispatch and algebraic pattern matching are both interesting, >> but having not used a language that implements either extensively, I >> can't say for sure if either are that sweet spot. >> >> Aaron Meurer >> >> >> On Sun, Jan 14, 2018 at 7:46 PM, Henri Tuhola <[email protected]> wrote: >> > Hello, >> > >> > I would directly want to reach people who have been developing sympy, so >> > I posted on your mailing list. >> > >> > Development of sympy on top of python has not been frictionless, and I >> > think you know that. Unfortunately only few people know what's even wrong >> > there. >> > >> > I am working on a new programming language and found out that the choice >> > of how to allow extension of arithmetic is a really tough problem to figure >> > out. Simply implementing arbitrary operator overloading and calling it a >> > day >> > doesn't seem to be so good choice. >> > >> > I wonder that if you had a choice of redesigning arithmetic in Python to >> > better support sympy, what would you attempt to solve? If you know what you >> > would do, I would gladly read that. >> > >> > Thank you ahead of the time. >> > >> > -- >> > You received this message because you are subscribed to the Google >> > Groups "sympy" group. >> > To unsubscribe from this group and stop receiving emails from it, send >> > an email to [email protected]. >> > To post to this group, send email to [email protected]. >> > Visit this group at https://groups.google.com/group/sympy. >> > To view this discussion on the web visit >> > https://groups.google.com/d/msgid/sympy/2ab5c94f-97f2-46d9-aa92-39839a68566a%40googlegroups.com. >> > For more options, visit https://groups.google.com/d/optout. > > -- > You received this message because you are subscribed to the Google Groups > "sympy" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To post to this group, send email to [email protected]. > Visit this group at https://groups.google.com/group/sympy. > To view this discussion on the web visit > https://groups.google.com/d/msgid/sympy/05adcf16-ef85-4101-87da-aa421d9dd4f5%40googlegroups.com. > > For more options, visit https://groups.google.com/d/optout. -- You received this message because you are subscribed to the Google Groups "sympy" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at https://groups.google.com/group/sympy. To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/CAKgW%3D6J-iYsd_TW%3DgE51pK3R4RPRQVe2-9Yakg8cHN5ZcDerhA%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.
