17 ms·
Weird Python Integers
- std_throwaway 9y agoSummary: Integers in python are full blown objects. Small numbers are stored in a central preallocated table where each entry represents one number. Setting a variable to a small integer makes it point to an entry in that table. Multiple variables may point to the same small integer objects in that table. Fooling around with the table leads to funny results.
- cma 9y agoMicropython does a much better job, using pointer alignment guarantees to pack in small integers: https://micropython.org/ https://micropython.org/ Apparently it is too hard to move python proper over to that method because of backwards compatibility issues with C extensions.
- zde 9y agoGuido said in some interview they used tagged pointers in some project before Python, and it didn't work well. Apparently there is some benefit in "value is always a reference" (less code paths?) that outweights somewhat larger heap pressure.
- concede_pluto 9y agoIt's just like Fortran, where everything including a constant is passed by reference, so if you assign to your arguments you might corrupt your program's only copy of 7 unless the linker put it in a read-only page.
- andars 9y agoCould you be more specific? Fortran passes arguments by reference, but writing to an argument won't mess up the "program's only copy of 7". For example, the first output of the following program is 4, but the second is still 7. program main integer m,n m = 7 call corrupt(m) write (*,*) m n = 7 write (*,*) n stop end program main subroutine corrupt (a) integer a a = 4 return end
- mastax 9y agoI think he means something like call corrupt(7) which causes a segfault for me. I suppose if you were using a compiler/platform that didn't store constants in read only memory, this might actually work.
- andars 9y agoBy manually moving the constants from .const to .data, I got this program: program main integer m,n call corrupt(7) write (*,*), 7 stop end program main subroutine corrupt (a) integer a a = 4 return end to output 4. Thanks for pointing this out, 'concede_pluto. Pretty weird!
- alephnil 9y agoAll newer versions of Fortran will segfault, but back in the days they would not. Back in the ninties I fixed a bug we found when porting a Fortran 77 program from HPUX to Linux. The program would segfault on Linux, but worked on HPUX. The reason was that in one subroutine, a parameter value was stored in a local variable, then used for computation and restored at the end. Since constants was stored in read only memory when using g77 on Linux, but not on the f77 compiler on HPUX, the Linux port would crash, but not the original HPUX version. In that the code you had above would have worked.
- emmelaich 9y agoYeah, a "fun" thing to do was to change the value of built-in constants such as pi.
- DonHopkins 9y agoThat feature was required by the Indiana General Assembly. https://en.wikipedia.org/wiki/Indiana_Pi_Bill https://en.wikipedia.org/wiki/Indiana_Pi_Bill
- 9y ago
- leipert 9y agoThe same is true for Java apparently: https://stackoverflow.com/a/2001861 https://stackoverflow.com/a/2001861
- chrisseaton 9y agoThat's only the case if you specifically treat them as full-blown objects. If you treat them as primitives they stay as primitives. Note that the type of these local variables is `Integer` rather than `int`.
- zeptomu 9y agoAlthough you are technically correct (the best kind of correct), it is still a shortcoming of objects (or maybe just their implementation in Java). It is just a bad idea to implement the '==' operator using comparison on addresses instead of on values and I consider languages pretty low-level that do that. In a sane language == is always a function adhering the attributes of an equivalence relation (reflexivity, symmetry and transivity), where a user can expect an actual value comparison - AFAIK one is advised to overwrite `equals` in e.g. Java, but I do not think this is good design. //edit: There is no obvious answer, as one could argue, that languages just use different names for the same concepts ('==' vs 'equals') and consider comparison by value or by reference more fundamental (and choose '==' for that).
- chrisseaton 9y agoWhy is value-equality inherently more sane than identity-equality, in an object-orientated language? Value-equality takes an unbound amount of time to compute, as well.
- zeptomu 9y agoI agree, that's why I revised my answer (see edit). Probably comparison by-value is more useful in higher-level and comparison by reference in lower-level languages.
- 9y ago
- joncrane 9y agoThe real ProTip is in the comments. :)
- eighthnate 9y agoI believe that's called "interning". It's done for strings as well.
- ghewgill 9y agoLots of good discussion at this Stack Overflow question (2008): https://stackoverflow.com/q/306313/893 https://stackoverflow.com/q/306313/893 (Python “is” operator behaves unexpectedly with integers)
- avyfain 9y agoThe case of strings is also pretty interesting: http://guilload.com/python-string-interning/ http://guilload.com/python-string-interning/
- woodrowbarlow 9y agothe python documentation [1] says the following: > The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object. So it should be possible to change the value of 1. I suspect the behaviour of Python in this case is undefined. :-) does anyone have any idea how they chose that range? it's a 262-wide block starting at -5, which seems incredibly arbitrary. [1] https://docs.python.org/2/c-api/int.html https://docs.python.org/2/c-api/int.html
- masklinn 9y agoYou could probably find out from the code's log. I'd guess the small positive integers are common due to e.g. iteration or len() of small collections and the like, and the very small negatives are due to things like error values.
- llccbb 9y agoThe small negatives might also be for indexing from the end of lists. `my_list[-1]` is super common.
- masklinn 9y agoGood point.
- jwilk 9y agoI did some git archaeology. Here are relevant commits and bug reports: * Initial commit with -1...99 cached: https://github.com/python/cpython/commit/842d2ccdcd540399501a918b9724d2eaf5599f39 https://github.com/python/cpython/commit/842d2ccdcd540399501... * Cache more negative numbers, -5...99: https://github.com/python/cpython/commit/c91ed400e053dc9f11dd30c84e2bb611999dce50 https://github.com/python/cpython/commit/c91ed400e053dc9f11d... https://bugs.python.org/issue561244 https://bugs.python.org/issue561244 * Cache more positive numbers, -5...256: https://github.com/python/cpython/commit/418a1ef0895e826c65d4113be5d86891c199e15d https://github.com/python/cpython/commit/418a1ef0895e826c65d... https://bugs.python.org/issue1436243 https://bugs.python.org/issue1436243
- lispm 9y agoLisp systems also have fixnums and bignums. For example a 64bit Common Lisp: MOST-NEGATIVE-FIXNUM, value: -1152921504606846976 MOST-POSITIVE-FIXNUM, value: 1152921504606846975 Fixnums are typically stored inline in data structures (like lists, arrays and CLOS objects). Bignums will be stored as a pointer to an heap-allocated large number. Data has tags and thus in a 64bit Lisp the fixnums will be slightly smaller than 64bit. Bignums can be 'arbitrary' larger and there is automatic switching between fixnums and bignums for numeric operations.
- junke 9y agoThe range varies across implementations: the above values correspond to 60 bits; most-positive-fixnum evaluates to 4611686018427387903 in the version of SBCL I am currently running, which corresponds to 62 bits of data.
- tomsmeding 9y agoYou can also do a similar thing in Java, as illustrated in this answer on CodeGolf stackexchange: https://codegolf.stackexchange.com/a/28818 https://codegolf.stackexchange.com/a/28818
- leibnitz27 9y agoIndeed - the integer boxing cache is even required by the language spec! http://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.7 http://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#... (here == means reference not value equality) "If the value p being boxed is an integer literal of type int between -128 and 127 inclusive (§3.10.1), or the boolean literal true or false (§3.10.3), or a character literal between '\u0000' and '\u007f' inclusive (§3.10.4), then let a and b be the results of any two boxing conversions of p. It is always the case that a == b."
- wyldfire 9y ago> That is suprising! It turns out that all “small integers” with the same value point to the same memory. We can use the Python built-in function id which returns a value you can think of as a memory address to investigate. Unfortunately this blog post seems to miss a great opportunity to show you how you should compare integers for equality -- using the equality operator `==` and not the identity comparison `is`. EDIT: odd, this post attracted a lot of downvotes. Please help me learn how this post could be improved.
- dvfjsdhgfv 9y agoFrankly, what's the point? This is common knowledge.
- int_19h 9y agoIt never ceases to amaze me that Python seems to be the only one that got this right - namely, that == should have value comparison semantics, for all types, and that some other (preferably distinctive enough, rather than something like ===) operator should always compare references. Then you look at something like C#, where == compares values for value types and references for reference types, except that == is overloaded for some "value-like" reference types like String... it's a mess. OTOH, Java is consistent in a sense that == never dereferences, but awkward in practice because of the need to use equals() for strings, which is too verbose for an operation that's far more common than object identity comparison.
- andrewjw 9y agoCorrect me if I'm wrong, but doesn't C++ use == for value comparison?
- alphaalpha101 9y agoC++ uses `==` for whatever you want. But generally in C++, user-defined types will be documented as either having 'reference semantics' or 'value semantics' and all aspects of the type will conform to that.
- throwaway613834 9y agoDoes anyone know why Python refcounts everything -- even small integers, True, False, None...? Why not avoid it?
- chrisseaton 9y agoI don't know the internals of Python, but maybe checking if you need to refcount something is basically takes as long as actually just going ahead and doing it all the time anyway.
- throwaway613834 9y agoYeah you might be right, it's hard to say. Generally I view writing as more expensive than reading so I don't know.
- gvx 9y agoNote that it's not just write vs read, it's write vs read + branch.
- throwaway613834 9y agoRight, if it was just 1 read vs. 1 write then I would know!
- pythonaut_16 9y agoThat's also the same thought behind the Python practice of "it's better to ask forgiveness than permission". If you're going to have a conditional that will evaluate to True most of the time, sometimes (in Python) it's considered better to just go ahead and try the logic that the conditional is guarding, and then catch the exception and handle it if it happens.
- jepler 9y agoSure: CPython doesn't know statically that anything is "not an object", so your proposal adds an extra test to every incref and every decref. This is perceived as probably worse for performance. In 2002, a hacked CPython interpreter (source code long since lost, sorry!) saw a 5% performance decrease on pystone but a 14% performance increase on an integer-heavy workload. https://mail.python.org/pipermail/python-dev/2002-August/027685.html https://mail.python.org/pipermail/python-dev/2002-August/027...
- santiagobasulto 9y agoNice article. I wrote a similar piece some time ago related to booleans, in case anybody is interested: https://blog.rmotr.com/those-tricky-python-booleans-2100d5df92c https://blog.rmotr.com/those-tricky-python-booleans-2100d5df... And to avoid issues with is/==, we recommend our students to always use == (except for `is None`). Also related piece:https://blog.rmotr.com/avoiding-being-bitten-by-python-161b063e7da2 https://blog.rmotr.com/avoiding-being-bitten-by-python-161b0...
- sevensor 9y agoThat's unnecesarily restrictive -- I've gotten great use out of "is". You wouldn't want to use it on an integer, but I've been using namedtuples a lot lately, and "LastKnownConfiguration is CurrentConfiguration" works great to to check whether anything has changed without checking all of the fields for equality.
- santiagobasulto 9y agoYou're very much right and it's a great suggestion. We just suggest that to our students when they're starting in order to help them avoid issues. But there are exceptions.
- sevensor 9y agoFair point -- == versus "is" is a pretty subtle distinction and best avoided by novices.
- zde 9y agoThat's not weird but pretty common knowledge. IIRC 1-char strings are interned too.
- boramalper 9y ago> We can use the Python built-in function id which returns a value you can think of as a memory address to investigate. > [...] > It looks like there is a table of tiny integers and each integer is takes up 32 bytes. It is the memory address but it's a "CPython implementation detail: This [return value of the id() function] is the address of the object in memory."[1] Though you cannot use this to determine the size of an object, or rather you "shouldn't" because that assumes a very specific implementation detail, which isn't there. If you'd like to get the size of an object, use sys.getsizeof().[2] Also keep in mind that containers in Python does not contain the objects themselves but references to them so the returned size is the size of the object itself only, non-recursively. Read "Is Python call-by-value or call-by-reference? Neither."[3] for some more details. [1]: https://docs.python.org/3.6/library/functions.html#id https://docs.python.org/3.6/library/functions.html#id [2]: http://docs.python.org/3.6/library/sys.html#sys.getsizeof http://docs.python.org/3.6/library/sys.html#sys.getsizeof [3]: https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/ https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-...
- int_19h 9y agoPython memory model is amazingly simple and consistent: everything is passed and stored by value, but all values are references to objects.
- Phrodo_00 9y agoI wouldn't say it's necessarily neither. While the concept of binding used in [3] is consistent, you can also simply say that functions are call-by-value and all values are references. Pretty much the same way C uses pointers when passed to a function. BTW, I didn't like the way the difference between mutable and immutable objects was pointed out, it sounded like the language recognized and supported those two types, when in fact the difference is just in the methods exposed by the objects. Even immutable objects can be modified if you don't respect the contracts (like in the case of this article).
- dsfyu404ed 9y agoYup, 0day hunters have fun with this behavior from time to time.
- jwilk 9y agoWhy is it interesting for "0day hunters" in particular?
- syncsynchalt 9y agoI believe it's because you can find code where it does the right thing when e.g. a username is short enough, but fails in a useful way if you increase an input string length.
- dsfyu404ed 9y agoIf you can write to memory in a way that overwrites integer values the whole logic of a program falls apart. Imagine if an adversary could affect the result of everything you do with integers. It's not necessarily a common thing. Most people aren't running python directly on the internet but being able to screw around with stuff like this is just another important tool in the toolbox when you're attacking something.
- ZephyrP 9y agogood guy "0day hunter" > has 4-byte arbitrary global write primitive > uses it to change arithmetic
- gre 9y agoIt's not weird, it's pythonic.
- adtac 9y agohttps://github.com/adtac/exterminate https://github.com/adtac/exterminate Plugging my near useless Python library that does this and a lot of other subtle, annoying things to break programs. The library is essentially a display of how much Python actually exposes to the user and how modifiable it is.
- rosstex 9y agoWhy does my inner self find it immensely fun to modify programs in subtle ways to watch how they break? Considering there's infinite ways to write junk code, and far fewer ways to make it work.
- hobolord 9y agoIt's probably what got me interested in programming in the first place. Writing C code and learning about pointers to memory, and playing around with memory locations to see undefined behavior could be invoked
- ygra 9y agoThat's the basic idea behind mutation testing which can turn the aim of 100 % code coverage up to 11. You introduce random mutations into the code and if a mutation does not cause at least one test to fail then either your tests aren't covering enough, or that expression/statement was unnecessary for correct program behaviour. Never got a chance to actually try it out, though.
- vog 9y agoWow, the alternative print is extra evil ... >>> from exterminate import AltPrint >>> print("Hey! How are you?") Yo dawwwwg! Wuz crackalackin' yo? ... because that string is not calculated locally, but retrieved via HTTP from an API call. So everything you print is sent to an external server. AltPrint calls Gizoogle.translate(): https://github.com/adtac/exterminate/blob/f18862ed5b2e143e1833f632c30bd5c15bc89687/exterminate/AltPrint.py https://github.com/adtac/exterminate/blob/f18862ed5b2e143e18... And Gizoogle.translate() performs an HTTP call: https://github.com/adtac/exterminate/blob/f18862ed5b2e143e1833f632c30bd5c15bc89687/exterminate/Gizoogle.py https://github.com/adtac/exterminate/blob/f18862ed5b2e143e18...
- mbell 9y agoRuby does something similar, but all Fixnum (native sized) values are 'fixed objects': a = 2**62 - 1 b = 2**62 - 1 a.object_id == b.object_id # true a = 2**62 b = 2**62 a.object_id == b.object_id # false Ruby does automatic promotion from Fixnum (native size) to Bignum (arbitrarily large) and uses one bit of the native size as a flag to identify this which is why 2^62 - 1 is the max instead of 2^63 - 1. Though I think this is only true of MRI and other implementations handle it without the flag bit. Perhaps one difference from Python is that in MRI Ruby Fixnum doesn't really even allocate an 'object', the object_id is the value in disguise. In fact all 'real' objects have even object_ids and all odd object_ids are integers: a = 123456789 (a.object_id - 1) / 2 # 123456789
- timonoko 9y agoI remember implementing this too on Nova 1200. When the address space is bigger than the memory, you can place those integers outside the memory. Those objects do not actually exist in other words. Saves you memory cycles too, because you can calculate the numeric value from the address.
- deleted 9y ago[deleted]
- jedberg 9y agoIn [7]: a = "foo" In [8]: b = "foo" In [9]: a is b Out[9]: True In [10]: b = "foobaljlajdfsklfjds l;kjsl;dfj ls;dfj l;skdj flsdjluejsklnm " In [11]: a = "foobaljlajdfsklfjds l;kjsl;dfj ls;dfj l;skdj flsdjluejsklnm " In [12]: a is b Out[12]: False Seems to work with small and big strings too.
- nneonneo 9y agoThis would be due to string interning, which automatically caches small strings, which are very common because attribute names are just strings.
- Dylan16807 9y agoTo be clear the question of which strings to intern is up to an implementation of string interning. Only small strings, all strings, or even only large strings are all options that are good for different reasons.
- winstonewert 9y agoRelevant code here: https://hg.python.org/cpython/file/f254ceec0d45/Objects/codeobject.c#l72 https://hg.python.org/cpython/file/f254ceec0d45/Objects/code... Its not actually length, its whether the string looks like it could be a python name, and it only happens for string literals.
- cpsempek 9y agoThis comment is great, points out why the article is bad. Python integers are not weird simply because a well-defined operator gives a result which goes against an intuition based on the semantics of English.
- tosh 9y agorelated read: Equal Rights for Functional Objects or, The More Things Change, The More They Are the Same (1993) by Henry Baker http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.23.9999 http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.23.9...
- rcthompson 9y agoIs there any practical reason to use "is" to compare two ints (other than demonstrating integer interning)? Should doing so produce a warning?
- yen223 9y agoI'll take it one step further - I don't think I've ever used `is` for anything other than comparisons to `None`.
- bobbyi_settv 9y agoIt's useful when you want a sentinel object and you don't want to use None as the sentinel because it is a valid value. Some discussion here: http://www.ianbicking.org/blog/2008/12/the-magic-sentinel.html http://www.ianbicking.org/blog/2008/12/the-magic-sentinel.ht...
- gvx 9y ago`is` is also useful for things like detecting cycles for walking object graphs. But I'd estimate 99% of the times I've used `is` was for comparisons to `None`, and 90% of the rest of times for comparisons to other sentinel values.
- DonaldPShimoda 9y agoOnly if you want to determine whether two integers are the same object and not just two objects that look similar. `is` is for identity, whereas `==` is for equivalence. Idiomatically, you generally only see `is` used for comparison to `None`. Sometimes developers want to check identity, and you'd use `is` there too (but I don't have a handy example offhand).
- bobbyi_settv 9y agoA lot of code relies on the fact that "is" is very fast because it just compares memory addresses. In order for it to give a warning when it is used on integers, it would need to also look at the types of the objects which would have have some performance impact. No, it is not good practice to rely on the interning of ints and short strings. It's implementation-specific behavior and relying on it can cause your code to not work on PyPy or even potentially a future version of CPython.
- wgrover 9y agoYou can use sys.getrefcount() to explore these "weird integers": https://news.ycombinator.com/item?id=15093897 https://news.ycombinator.com/item?id=15093897
- squeaky-clean 9y agoI wrote a blog post about this in the past. It's really fun going through the oddities of the language like this. It caches small integers, but also literals used in the same interpreter context (I'm probably getting that last term wrong). You'll get different results if you run these in from the shell as opposed to executing a script, try it out! Here's a fun example >>> x = 256; x is 256 True >>> x = 257; x is 257 True >>> x = 257 >>> x is 257 False >>> def wat(): ... x = 500 ... return x is 500 >>> wat() True
- kmill 9y agoThe Python interpreter compiles programs into bytecode first, and the bytecode includes instructions that load a constant from the constant pool. As an example, >>> def f(): ... x = 2222; y=2222 ... return x is y ... >>> f() True >>> f.func_code.co_consts (None, 2222) This last line is showing the constant pool, which is just a standard Python tuple. I believe the REPL compiles each input in a new toplevel module context, so each input gets its own constant pool. Functions get their own constant pools, which explains the following behavior: >>> if True: ... def f():return 2222 ... def g():return 2222 ... >>> f() is g() False
- squeaky-clean 9y agoYep yep, and only when no operations are done to it, otherwise it will end up storing the same constant multiple times and referring to each separately. For example >>> def f(): ... x = 2221+1; y=2221+1 ... return x is y >>> f() False >>> f.__code__.co_consts (None, 2221, 1, 2222, 2222) >>> dis.dis(f) 2 0 LOAD_CONST 3 (2222) 2 STORE_FAST 0 (x) 4 LOAD_CONST 4 (2222) 6 STORE_FAST 1 (y) 3 8 LOAD_FAST 0 (x) 10 LOAD_FAST 1 (y) 12 COMPARE_OP 8 (is) 14 RETURN_VALUE Not too sure when stuff like this would be useful (it goes way beyond the 'avoid using is unless checking references` advice), but it sure is fun.
- 9y ago
- Aliyekta 9y agohttps://stackoverflow.com/questions/132988/is-there-a-difference-between-and-is-in-python/1085656#1085656 https://stackoverflow.com/questions/132988/is-there-a-differ...
- jonbarker 9y agoCool examples, but I'm not super concerned about the problems arising from the ability to 'use ctypes to directly edit memory'. It's actually pointers to memory blocks, not the memory contents itself: https://docs.python.org/3/library/ctypes.html https://docs.python.org/3/library/ctypes.html If you're advanced enough to need to handle pointers to memory blocks in your python program, you are probably good enough to know not to create problems with the behavior of iterators on ranges.
- sl4i6j3o4i98g 9y agoBrilliant! Very interesting and an insight into the inner workings of python. Thank you for sharing Kate!
- mattbillenstein 9y agoStumbled across this when debugging a reference leak with a C-extension once -- small integers didn't exercise the problem, but larger ones did...
- tahoemph999 9y agoThis is not about comparing values. "is" compares address (memory location) and == compares value. While the same address implies the same value (for integers; see Naan for fun counterexample otherwise) the same value doesn't imply the same address.
- supertramp_sid 9y agoa = 1000 b = 1000 a is b The output is false only if you use the REPL. Run a .py script writing the above code and it returns True. I read about it on SO but can't find the link to it!
- knutae 9y agoFor comparison, integers in clisp: [1]> (eq (expt 2 47) (expt 2 47)) T [2]> (eq (expt 2 48) (expt 2 48)) NIL Explanation here: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node17.html https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node17.html
- rurban 9y agoBut nobody would use eq in lisp for this. For numbers you would use equal, comparing the values. You only would use eq for addresses, like cons cells or vectors. E.g. in php there is == vs ===. re python: this is of course superlame. the compiler can easily detect "is <int>" and forbid the const int table optimization. Only "is None" would make sense.
- DonHopkins 9y ago"Is", "is." "is"—the idiocy of the word haunts me. If it were abolished, human thought might begin to make sense. I don't know what anything "is"; I only know how it seems to me at this moment. — Robert Anton Wilson, The Historical Illuminatus Chronicles, as spoken by Sigismundo Celine. https://en.wikipedia.org/wiki/E-Prime https://en.wikipedia.org/wiki/E-Prime Kellogg and Bourland describe misuse of the verb to be as creating a "deity mode of speech", allowing "even the most ignorant to transform their opinions magically into god-like pronouncements on the nature of things". Bourland and other advocates also suggest that use of E-Prime leads to a less dogmatic style of language that reduces the possibility of misunderstanding or conflict. Alfred Korzybski justified the expression he coined — "the map is not the territory" — by saying that "the denial of identification (as in 'is not') has opposite neuro-linguistic effects on the brain from the assertion of identity (as in 'is')."
- logicchains 9y agoThis is part of the reason why I prefer structural types (like in Typescript, Go and OCaml) over nominal types (like in most languages), as any object with the required methods is automatically an instance of such a type, instead of having to explicitly declare that it "is"/"extends"/"implements" that type. I suspect that with dependent types, nominal types are actually a degenerate type of dependent structural type: a dependent pair of a type and a proof that a string 'name' field has some particular value, or that a list<string> 'implements' field contains some particular string (the interface name).
- DonHopkins 9y agoWhich is a great approach if you're slinging JSON between different languages and implementations. Python is fine with duck typing.
- logicchains 9y agoIt's also really good for testing: if you want to mock some opaque third-party object, you can just create an interface with the same methods as that object and use it in your code. Nominal types often don't allow this, such as how in Java you can't make a class to which you don't have the source implement your own interface, or make it painful, like orphan instance restrictions in Rust and Haskell.
- Veedrac 9y agoNow change it back!
- anentropic 9y ago"things you should never do"
- d0mine 9y ago>>> 42 == 0 True https://lameiro.wordpress.com/2010/07/18/deixando-o-interpretador-python-maluco/ https://lameiro.wordpress.com/2010/07/18/deixando-o-interpre...