Redefining x - y = 0

I just realized you can do this in Julia:

x - y = 0

subsequently breaking the minus sign. I had a bug in my code where I wrote - instead of _ with the intention of defining a variable, and broke - instead. It was a bit hard to find.

Should there be a warning or something when you overwrite a Base function like this?

Its a feature :stuck_out_tongue:

Infix operator definition syntax needs documentation · Issue #15483 · JuliaLang/julia · GitHub

To avoid this kind of typo we could require spaces like here

julia> false?0:1
ERROR: syntax: space required before "?" operator

julia> false ?0:1
ERROR: syntax: space required after "?" operator

julia> false ? 0:1
ERROR: syntax: space required before colon in "?" expression

julia> false ? 0 :1
ERROR: syntax: space required after colon in "?" expression

julia> false ? 0 : 1
1
1 Like

I encountered this same issue in my code a few years ago and complained about it on this discourse. It comes up about twice a year. I proposed that there should be some extra syntax required when defining a type-pirating “core” operation that already has a definition in Base. I argued that imposing extra syntactic requirements in this case is necessary to prevent users from shooting themselves in the foot. However, the core-devs disagreed with me. To paraphrase, they said that since this is perfectly legal Julia behavior, there is no reason to make anyone jump through a syntactic hoop to harness it, and too bad for the users who accidentally redefine methods because of a typo. I continue to believe that extra syntactic clues should be mandatory in this case, but it is probably too late to revisit the issue.

1 Like

That is not a fair nor correct “paraphrasing”. The thread where the discussion took place has already been linked, no need for you to go and put words into peoples mouth. Just link to the dicsussions instead.

1 Like

You are absolutely correct, and I apologize for putting words in other people’s mouths. There are 4 or 5 different discourse threads bearing on this issue; I will try to track them down and post links to them in the next couple of days.

The main objector was @jeff.bezanson and he makes the case that uniformity of syntax is more important than making ad hoc syntax exceptions. At no point did he make any statements about not caring about users. In any case, changing this would be breaking, so there’s not much point in discussing it except in the context of Julia 2.0. I don’t think that it would be necessary to dig up links.

1 Like

I guess you could write your own little function that reads a .jl file as a string and then goes hunting for common typo char patterns, maybe highlight the bits it finds with printstyled. It probably wouldn’t need to run that often though

First, I want to apologize again. I misquoted the core-devs and simplified a complex issue into an inappropriate and demeaning sound-bite. I found two previous threads on this topic; there are maybe 2-3 others that I can’t find right now.

With respect to Stefan Karpinski’s comment about not caring about users, I believe that the core-devs care very much about the user community. Indeed, Stefan personally deserves a medal for his patient and careful explanations on this discourse site! And the core-devs make huge numbers of other contributions to the user-base, including answering issues on github, arranging JuliaCon… the list is very long.

This issue is not about caring versus not caring about users; rather it is about balancing competing interests of two different groups of users. For the purpose of explanation, let me propose a straw-man “new syntax rule” (NSR): If a function g defined in an inner scope has the same name as an existing function g in an outer scope, then at least one argument of the inner g must have its type qualified (::Any is OK). Zero-argument functions are exempt.

For a power user who writes code-generation macros and other forms of introspective code (user group A), NSR would be a hindrance and an annoying speed bump.

For a Julia newbie, for a university-classroom user, and for a person prone to typos (user group B), NSR could save that person an hour or two of chasing down a “spooky-action-at-a-distance” bug caused by a typo.

My contention is that the current state of affairs in which a one-character typo can redefine an operator favors user group A too much over user group B.

3 Likes

I thought this kind of definition would be a good joke for April Fool’s day. Just put something like x + y = "Send bitcoins to get your computer back..." into the beginning of your friends’ .juliarc. This shadows all definitions of +, so you can enjoy the show.

I got a bit confused here, because I thought you had to import - before extending it. But it’s more complicated than that.

<Starting a new Julia instance>

julia> -(a, b) = println("Bye")
- (generic function with 1 method)

julia> 2 - 3
Bye

<Shutting down and restarting>

julia> 2 - 3
-1

julia> -(a, b) = println("Bye")
ERROR: error in method definition: function Base.- must be explicitly imported to be extended
Stacktrace:
 [1] top-level scope at none:0

I was expecting the error in both scenarios.

The first one creates a new function named - in the REPL module Main and does not modify the core - function. In the second session you use core - in the REPL first so it cannot subsequently be shadowed.

1 Like

Yes, that would have been my guess.

I just thought that all exported functions in Base (or Core?) would have been loaded into Main, and could therefore not be shadowed. That would have made the current problem unlikely to occur.

Why isnáş— this the case?

Because it is very nice to be able to define module local functions with the same name as functions in Base.

3 Likes

As far as I can see, this is nice mostly when do not shadow the function, but rather extend it, as in

struct Q x::Int end
Base.:(-)(x::Q, y::Q) = Q(x.x - y.y)

What use case could you have for shadowing (instead of extending) a function like +? Or in general, shadowing any loaded function (even if it has not been called)?

Wouldn’t it be safer that shadowing required special syntax?

No, I often shadow Base functions (maybe not + but say size or something). There is also packages like JSON.jl that have their own JSON.parse function which is distinct from Base.parse for example.

No? It is much safer that special syntax (import Base: * or Base.:*(...)) is required when extending since that effects the global behavior.

1 Like

As others have said, defining a function with the same name as some other function in Base is occasionally useful (note that unless you import, you are not adding/overwriting a method to the original function).

After tracking down a bug, especially if it is hard to find, it is tempting to think of various safeguards that would have prevented it, but this is not always a good guide for refining a language; as one can easily end up with a straitjacket replacing a powerful language.

3 Likes

Maybe a warning could be issued by a linter, at least for simple cases like when it sees a-var=2, which can be a typo for a_var=2.

2 Likes

I think that the linter should tell you to write

a - var = 2

instead of

a-var=2

Then you would discover the bug.

It’s a really bad habit to mash variables and operators together like that. It makes code hard to read, and, as demonstrated in this thread, more bug-prone.