A Plea for Bit-Twiddlers! (please keep ! as logical negation, separate from bitwise negation!)

The fact that & and | operate on Bool is actually an argument in favor of also allowing ! to operate (as a logical operator, not bitwise) on Integer types (converting non-zero to false, and zero to true).
If Julia supported primitive types that were not a multiple of 8 bits (something like that which would be great for dealing with fields in C/C++/etc. structs), then Bool could be implemented as primitive type Bool 1 end instead of making it a UInt8 under the hood.

I disagree, I think that allowing non-booleans in contexts one expects booleans makes it easy for bugs to slip though. This was an unfortunate choice in C, but also in the Lisp family (anything not nil is true), and many other languages that follow that pattern. Julia chose to have a Bool type, instead of repurposing some other type or allowing anything to be interpreted as a boolean with a bit of imagination.

I also rather like the !!x == x invariance, which ! operating on Ints in the manner you suggest would break.

2 Likes

Where is that an invariance? Iā€™ve never seen that described as a property of ! in any language before.
Using !!var is an old C idiom, to convert an integer in var to 0 or 1, instead of (var != 0), 2 characters vs. 7.

In Julia (at least that is my impression), now for ~ and ! separately (unless I missed some case), but after the merge for !. I find this rather neat.

And a quite horrible one IMO. One of those 1000+ C idioms where a string of characters does what you want, but for some reason people refrain from naming it for that purpose. If you know the idiom, you can reverse engineer intent, otherwise not. Truly an idiom in the sense of

an expression whose meanings cannot be inferred from the meanings of the words that make it up

Some people consider such idioms bad style.

2 Likes

Excuse me to put it this way but I disagree on your disagreement.

Itā€™s not an idiom in that sense at all. Any C/C++/ā€¦ programmer knows that ! will convert 0/non-zero to 1/0 (true/false), and that doing that twice would give you 0/1.

Changing this in Julia I think is the sort of thing that can lead to bugs, when pretty much every other common language (except Rust) has ! and ~ with separate meanings for logical and bitwise.

This is the same as the problem of false cognates when learning a foreign language, for example when somebody tries to say ā€œembarrassedā€ in Spanish, and comes up with ā€œpregnantā€ (embarazada).

If people are so dead set on the desire to free up ~, then at least donā€™t conflate bitwise and logical for !, and change ~ to Ā¬ instead (and possibly free up & and |, and move those to āˆ§ and āˆØ, so the bitwise operators are consistently Unicode operators in Julia.).
At least then, people reading bit-twiddling code in Julia would probably recognize the meanings of those Unicode operators, or at least know to look them up, instead of seeing ! and believing it is doing something that it does not.

1 Like

Right, only math guys ever multiply numbers by 2. Most programmers would never do anything like that, ever.

5 Likes

I think most programmers donā€™t mind the extra character - and would rather not worry about all the strange cases that using juxtaposition as syntax causes.
TBH, for mathematicians in Julia, the justification though doesnā€™t seem to be saving one character, but rather, make stuff look like math.

I should be clear, Iā€™m not trying to say thatā€™s necessarily a bad thing, itā€™s just something that needs to be thought about a lot in advance, all the pros and cons of something like using juxtaposition, or changing an operator that has pretty much the same meaning on all major languages, just to free up one, for ?.

Julians are lucky in that they can use any number of Unicode operators for anything new, without making code confusable by being almost, but not quite, the same as most of the other languages.

I love the juxtaposition syntax, even though itā€™s to a large degree about aesthetics. Itā€™s not just one character, though. Itā€™s all those cases where I can change

a/(2 * n)

into

a/2n

Yes, like I said, I wasnā€™t trying to say itā€™s a bad thing, and probably the right choice for a language with a focus on being able to do math well, but for people that donā€™t have a lot of *s in their code anyway (I have almost none), having the code look like math or saving a few characters in that case is not a big deal, while some of the weird edge cases in the syntax cause more problems.

For bit twiddlers used to C, being able to test for 0 vs. non-zero with a simple ! feels the same, the code feels rather clumsy and inelegant without it.
(Nicely enough with Julia :slight_smile: , as I said before, a one-line bit of piracy means my code still looks nice and succinct to me, by making ! work on Integer, where otherwise it would just have been a MethodError)

A good way to do this is to define

!x = Base.:!(x)
!(x::Integer) = (x == 0)

in your code. Then thereā€™s not even any piracy.

5 Likes

I guess Iā€™m missing something, how is that definition different from:

(waiting to learn from the Jedi (oops) Julia Master!)

Edit: maybe I get it now, itā€™s overwriting ! in my package, not extending it, right?

Yes, itā€™s creating a new ! inside your module that defaults to Base.! behavior for everything but Integers. This way everybody else who uses ! will continue to get the standard behavior (unless they import ! from your module).

The one difficulty is Iā€™d like to make that definition easily available by just using a module, and I donā€™t see how to do that without extending Base.:!

You would have to say using Foo: !.

I thought that would give a conflict (you know, use must use Base.xxx and Foo.xxx)

Folks - let me know what you feel about my proposal, consolidated from otherā€™s ideas here and on GitHub, and join in the Tomatina (a festival in Spain of throwing tomatos) :grinning:.

!(val::Integer) = (val == 0) Please donā€™t do this. I love that values non-booleans donā€™t have magic comparison properties like in Python, itā€™s just too many special cases to remember. Also it would be weird if if !a end worked for a variable where if a end errors.

9 Likes

If ! is used for both logical and bitwise not, couldnā€™t foo &= ~0x00ff become foo &= !0x00ff which is equally concise?

Special cases? I actually think of it as being a very generic rule, no special cases at all. ! returns a boolean based on the type of the value. That seems very Julian to me. Just like the relational operators can be used with many different types of values, but they always return a boolean value.
Just as if iszero(a) ... works, even if if a doesnā€™t, so should if !a work.

The problem there is that anybody used to most of the languages in the world expect that ! returns a boolean value, and never anything else (just the Rust people donā€™t, that Iā€™m aware of so far)

I think that foo āˆ§= Ā¬0x00ff would be more likely to be understood, people arenā€™t used to using those Unicode operators, but they may very well know that they are boolean operations. foo and= not(0x00ff) is also less likely to confuse people coming from the many C-like languages.
(there are many languages that in other aspects are not at all like C, but that follow Cā€™s conventions for logical and bitwise operators, like PHP, Perl, Ruby, or some or most of them, like Python or Rust).