Is it possible to create `and` and `or` extension for `Bool` type?

i now see what you mean:

func && (a: () -> Bool, b: () -> Bool) -> Bool

or even this (pseudo code):

func && (a: Any, b: Any) -> Bool {
if a is Bool {
....
} else if a is () -> Bool {
....
}
.....
}

Well, one of them need to be evaluated regardless. So we probably need only one closure.

Be careful though it may affect type checking.

yes ;-( i wish i could express myself better to the compiler:

func foo ( a: Bool or () -> Bool)

in this case probably i can create two:

func foo (a: Bool, b: () -> Bool) and
func foo (a: () -> Bool, b: Bool)

While emoji identifiers do seem silly, restricting to ASCII is a huge limitation, especially to foreign developers whose native language uses non-Roman glyphs. Supporting Unicode is the right thing, even if that allows for silly identifiers.

4 Likes

IMHO, it was never a huge issue or a show stopper for anyone. not like "i wanted to be a software developer but i decided not to as i don't know english". doctors used to know latin, basketball players known to be tall... programmers need to know latin alphabet and a few english words like "protocol" or "while", etc. the worst thing that can happen is they will put mistakes in their identifier names in their own code or use non existing english words for example words transliterated from the language they know. allowing unicode in identifiers in a language like swift seems to be due to a "me too" syndrome (if other modern languages do that why not us), makes language larger and heavier while providing few benefits and opens a whole class of cans of warms -- below actually compiles:

let ΝΟΤ_ᎳНАТ_ІТ_іѕ = 1 // non roman characters
var x+y=z = 0
x+y=z += 1
if x+y=z == 1 {
    print("it works")
}

If you’re dead-set on replacing && and || in your code, may I suggest:

func all(_ conditions: Bool...) -> Bool { !conditions.contains(false) }
func any(_ conditions: Bool...) -> Bool { conditions.contains(true) }
func none(_ conditions: Bool...) -> Bool { !conditions.contains(true) }

These are equivalent to the logical operators:

if all(true, false) { ... } // true && false
if any(true, false) { ... } // true || false
if none(true) { ... }       // !true

But they add some flexibility by allowing you to specify fewer or more parameters than the operators do. (You do lose @autoclosure, though.)

3 Likes

nice )

not as generic but with auto closure support:

func all( _ a: Bool, _ b: @autoclosure () -> Bool, _ c: @autoclosure () -> Bool = true, ... ) -> Bool

While not exactly on docs.swift.org, it's a clear violation of the principle of least astonishment. And the Swift docs do mention a general rule to "embrace precedent".

Adding new syntax is far less dangerous than changing the semantics of existing syntax. For anyone who expects to ever work on a team, adhering to existing conventions has enormous value, as it makes source code safer, less error-prone and much easier to reason about.

1 Like

if it is dangerous it shall be enforced at the language level. example:

operator & (dontAutoclose, dontAutoclose): MultiplicationPrecedence // inside swift
...
user code:
func & (a: SomeType, b: @autoclosure () -> SomeType) -> ... // Error. shall not use auto closure here otherwise it is a violation of the principle of least astonishment

Non sequitur.

Whilst of course you are free to code however you want, bear in mind that if you are ever going to be working on code with other people, they are probably not going to appreciate your language "adjustments". Adding a custom operator for something as simple as boolean expression introduces needless complexity for very llittle gain.

that’s obvious :)

say, you are tasked to do [undisclosed] and you chose to do this:

operator **

you ensured that system doesn’t use this symbol and your “func **” doesn’t use autoclosure. over time your code might start depending upon that fact.

later on you happened to do [undisclosed] and were lazy to do it yourself - you got a third party library to do that. or it could be a library provided by another team in your company. it so happened that they used the same symbol for their operator, so you immediately got a compilation error of duplicating operator. instead of changing your symbol because of [undisclosed] you checked their operator and their func - it so happened that they use the same priority and their func is also non autoclosing it’s operators, and by the nature of [undisclosed] you realise that their operation is similar to yours (say, like + is similar for floating point and integers) - you just commented your version of “operator **”. few weeks later you already forgot about it and updated the version of that third party library. everything still compiles. however the new version changed it’s func so now it has autoclosure for some reasons. if your code has assumptions in that regards it will be caught by surprise so would be you.

swift have predecents of either not providing unsafe (aka dangerous) features or making them explicit. in this example, should the autoclosure behaviour be recorded at the operator level swift would not compile the code immediately alerting you. and there could be many reasons for such mistake, be it lack of experience or a bad actor or a simple slippery, or something more involved as the example above. swift helps us avoiding dangerous stuff, so if that is indeed perceived dangerous by the community it needs to be checked at the language level.

i guess i am entitled to answer that as i did my custom <> and ~ operators.

first, i do not necessarily agree that language adjustments / corrections like these can be done by, say, "core swift team only". to give you an example: back when swift was in its infancy it immediately felt obvious to me that enum constants shall be camel case (they were pascal cased back then). i did that "correction" to my code right there and then before waiting for the fix to appear in the language. that's kind of moto i follow - see something wrong - fix it right there and then. helps me a lot.

second, there are things called "company coding rules". you'd be amazed how many such rules i saw that to me felt "absurd" (and obviously for those who created them they were totally fine). but then it's a simple matter of either adhering to company rules or quitting, and everyone is free to make that choice.

OP can speak for himself, but he already did say what was his motivation. in my case the motivation was to only have ! for dangerous operations. to me that's huge, i can now see the forest behind the trees.

i do work with other people and what probably helps me is that i also happened to be that team leader who establishes those company rules. so other team members may appreciate the rules or not, if they are not happy they are free to speak to me and quit :slight_smile:. so far nobody complained.

1 Like

Of course the core team has made and will continue to make bad design decisions. They are human after all. And the language will evolve. This is a good thing.

Just as natural languages also evolve, and we don’t speak Shakespearean anymore. This is also a good thing.

But even though thing can and will change, it is very useful to speak and write code in a way that is understandable to other people. That’s the whole idea of communication. And just as with words, it’s easier to add new words, and even tweak and existing words; than it is to completely overload existing words with new meaning.

Adding <> is strange and non-idiomatic, but not harmful. There is prior art in the field of programming, and it has no other usage in Swift already. New users may not understand, but they probably won’t jump to conclusions and misunderstand either. They’ll probably ask, learn, and move on.

But overriding & is probably much more harmful. It will confuse some people, make them make incorrect assumptions, which may in turn lead to bugs. At the very least, it will cause confusion and hinder productivity.

1 Like

on the contrary:

func & <T> (a: Set<T>, b: Set<T>) -> Set<T> {
return a.intersection(b)
}

let setC = setA & setB

That’s not a counter example. A set could both be considered a predicate, but also it can be considered a mask. And at callsite you cant easily see the operator definition and your variables are more often than not named like a noun in plural form.

But whatever. Feel free to go against best practices and established conventions on your team. ¯_(ツ)_/¯

3 Likes

you lost me here.

var a, b: Set<Flavour>

let c = a & b

names are not shown, so what. whether you use a particular convention for prefixing/suffixing names or otherwise encoding the type information in names is up to you and is an orthogonal issue to this discussion.

a = b + c // is a an Int, Double or a String? or maybe your own type?

please note that inside the very language definition they provide this example:

i can't see how "+" is miles away from "&" and how overloading either of those can be perceived as going against the best practices.

thank you, i am so relieved now! :slight_smile:

The difference is convention. One is conventional, it does what you know and expect. The other is non conventional, and adds completely new meaning to the operator.

I’m not saying overloading in general is bad. But that overloading the semantics is bad. In fact; actively harmful. Adding a bitwise & for bit sequences and bit vectors other than Int is a non-issue as long as the semantics are respected.

I’m not speaking of using names to encode type information, but again to encode semantics. This whole discussion is about readability, and maintainability of code. Names matter. In fact, the Swift guidelines spends several paragraphs on naming.

You’re right that naming is orthogonal to operator usage, but when the operator has several competing and non-compatible semantics, it undermines readability to also use non-descriptive names.

One could argue that it’s clear which overload is used in let two = evenNumbers & primeNumbers and which is used in let clipped = image & mask. But when a reader reads let c = a & c, there’s no way to know, without scanning up and down the screen, or using your IDE for assistance.

You can read that as an argument for your overload, but against your code sample. Using good names can certainly help make it obvious what’s going on, which makes the pain of overloaded operators more acceptable. However, the opposite is also true: When overloading operators with new meaning, you have to be more explicit around your operators, and you can’t get away with letting your operator communicate intent for you.

I spent several replies trying to explain that the semantics of boolean logic and bitwise operations are different, but it doesn't seem anyone understood.

6 Likes

Maybe that is because of the explanations that the semantic is identical :slight_smile:
That does not mean that there can‘t be a good reason for two operators, though:

I think that difference makes sense in many cases.
Additionally, I guess minimal evaluation could be irritating for (multi)-bitwise operations (just as it can be irritating for booleans ;-)