ERROR_THREAD_EXISTS
-
-
@levicki said in ERROR_THREAD_EXISTS:
There is absolutely no good reason the void type exists
Yes there is. Backwards compatibility. Because omitting a type (used to) imply
int
. (C++ now flags it as an errror)
-
@levicki said in ERROR_THREAD_EXISTS:
@Gąska said in ERROR_THREAD_EXISTS:
To denote there's no value to be had. Duh.
Which you could do by simply omitting the type qualifier.
Except, as explained in parts of my previous post that you apparently skipped because they were too hard to understand, you'd still need the
void
type for situations other than function declaration, where omission is not an option.Instead, they have chosen to default to int for unspecified return type.
Wrong. There is no such thing as defaulting to int for unspecified return type in C++.
@Gąska said in ERROR_THREAD_EXISTS:
Or do what C++ did - make not-returning-anything a type to simplify the language
How is having to type
void method(int a, int b)
as opposed tomethod(int a, int b)
simplification???Less special cases = less grammar rules = simpler by definition.
@Gąska said in ERROR_THREAD_EXISTS:
That you invoked language consistency is the ultimate proof you don't understand this thing. A single syntax for two almost identical things IS more consistent
How is having the ability to write
void method(void)
andvoid method()
interchangeably when you are not passing any parameters, but not justmethod()
when you don't want to return anything more consistent?void
parameter list is a backward compatibility artifact and it sucks and everyone knows it - that's why nobody uses it. The difference between parameters and return value is that there can be 0 or more parameters, so you're not omitting any types on any parameter declarations, you're just declaring there are no parameters. Whereas in this alternate reality where function return types default tovoid
, not writing the return typeThere is absolutely no good reason the
void
type exists.Except for all the reasons I listed and you completely ignored. How do you imagine writing a function pointer without return type? And I don't even want to know what would happen with the most vexing parse.
-
@Gąska said in ERROR_THREAD_EXISTS:
especially making generic programming easier - say, how would you implement
std::function<void, Args...>
if there was novoid
?std::procedure<Args...>
, of course. Duh!
-
@levicki said in ERROR_THREAD_EXISTS:
Instead, they have chosen to default to int for unspecified return type.
Ah yes, the good old K&R C++.
-
@Gąska said in ERROR_THREAD_EXISTS:
void
parameter list is a backward compatibility artifact and it sucks and everyone knows it - that's why nobody uses it.It is necessary in C where, because of backward compatibility concerns, functions with empty argument lists take an arbitrary number of arguments. And have no (portable) way for the arguments to be accessed. It is really ugly. Declaring the arguments as
(void)
stops that nonsense; cleaning up this stuff was the single best thing about C89. (K&R C also defaulted argument types toint
. Ugh.)C++ doesn't replicate that mistake, but instead allows
(void)
so that post-C89 headers can be used. That is an ugly special case, but much less ugly than the original fuck up back close to 50 years ago...
-
@levicki said in ERROR_THREAD_EXISTS:
not a single compelling reason for void to exist which could not be solved in some other less retarded way.
Let me try again - third time's the charm. Template function that takes arbitrary function as argument, such as
std::bind
. Without code duplication.
-
-
@levicki said in ERROR_THREAD_EXISTS:
All I am getting from your replies is "backward compatibility", "special cases",... not a single compelling reason for void to exist which could not be solved in some other less retarded way.
Could you specify one of those less retarded ways?
-
@levicki said in ERROR_THREAD_EXISTS:
@Gąska said in ERROR_THREAD_EXISTS:
Template function that takes arbitrary function as argument, such as std::bind. Without code duplication.
So you are saying it is impossible without
void
? Got it.You're not even going to argue against that? Boring.
I wonder how languages without
void
type are handling that? Oh wait... they either have procedures for when they want to return nothing,And do these languages allow passing a procedure to a function accepting arbitrary function in its argument?
Thought so.
or simply return 0-tuple.
Which is pretty much equivalent to
void
, type theory-wise. I thought you said returning something when you mean nothing is retarded?But yes, we should really strive to keep backward compatibility with Algol68.
How's template metaprogramming about backward compatibility with Algol68?
-
@levicki said in ERROR_THREAD_EXISTS:
So you are saying it is impossible without
void
? Got it.I wonder how languages without
void
type are handling that? Oh wait... they either have procedures for when they want to return nothing,Which is a useless special case because instead you can just do this:
or simply return 0-tuple.
Which is exactly what we've been arguing is better instead. To wit:
@topspin said in ERROR_THREAD_EXISTS:
return
struct Void {};
Which, from a theoretical point of view, is basically the same thing (and why I named it the way I did). So you actually agree.
Let's look at Python for a different example from C. There's also no distinction between functions and procedures, a "procedure" is just a function that doesn't return anything, i.e., it returns
None
. It can do so either explicitly byreturn None
, or implicitly by justreturn
or running of the end of the function. Functions are defined bydef
, no need for two different keywords likefunction
andsub
orprocedure
.
The advantage Python has over C here is thatNone
is an actual value of typeNoneType
, so you can always assign it to a variable. You can't do that in C/C++:void x = f();
is an error, which sucks for C++ templates. (That's what the "regular void" proposal I mentioned was supposed to rectify: make void a regular type.)
-
@levicki said in ERROR_THREAD_EXISTS:
@topspin I think there is now something called
std::monotype
?Interesting. It seems to be called
std::monostate
but yes, same idea, it's a unit type.
The remaining problem for C++ is that it's a library-thing and not a first-class citizen of the language. So while conceptually that is exactly whatvoid
is, it (and all equivalent empty structs of different name) don't get the same treatment.The docs even list this usage note:
intended for use as a well-behaved empty alternative in std::variant.
showing that this what
void
should have been.
-
@levicki said in ERROR_THREAD_EXISTS:
@Gąska said in ERROR_THREAD_EXISTS:
And do these languages allow passing a procedure to a function accepting arbitrary function in its argument?
And that is necessary in how many use cases?
Strictly necessary? Never. Just like writing any computer program at all is never strictly necessary. Everything the computer can do, you can do by hand as well - eventually.
But it makes life MUCH easier in certain situations. And the more I do functional programming, the more it matters that I can pass arbitrary functions as arguments to other things and it all works regardless of type (or lack thereof).
"You don't really need it all that much" is the worst argument ever. If that's all you've got, I've already won. Because it's not about tradeoffs. There is literally nothing the language loses by having
void
keyword. The functionality of C++ withoutvoid
keyword (even with alternative syntax for declaring procedures) would be strictly less than the functionality of C++ withvoid
keyword. And it doesn't introduce any dangerous ambiguity either, unlike return-value-less function declarations which would turn the most vexing parse up to 11.Also why do you insist writing a template without
void
keyword would be impossible? It would just have different syntax.Propose this syntax then. Remember that this syntax for templates must be identical to syntax without templates because that's how C++ templates work.
@Gąska said in ERROR_THREAD_EXISTS:
Which is pretty much equivalent to void, type theory-wise. I thought you said returning something when you mean nothing is retarded?
To me () represents an empty result set, not an empty type. That at least makes some sense theory-wise.
Type theory-wise,
void
isn't an empty type, it's a singleton (one-value) type much like empty tuple. That's why you can return it in the first place. It's just that the language forbids you from reading the value or assigning it to a variable. Not the best it could be, but much better than making procedures and functions syntactically different.Note that adding real empty tuple to C++ would be quite hard, considering each lvalue in C++ is required to have an address you can read, and this address must be unique for every instance.
-
@topspin said in ERROR_THREAD_EXISTS:
The remaining problem for C++ is that it's a library-thing and not a first-class citizen of the language. So while conceptually that is exactly what void is, it (and all equivalent empty structs of different name) don't get the same treatment.
See last paragraph above. Any implementation of a real unit type - in library, in language, whatever - would either require extensive changes across most of the 1836 pages of the C++ standard so no memory needs to be allocated for it, or allocate at least 1 byte for each instance of that unit type.
-
@Gąska said in ERROR_THREAD_EXISTS:
@topspin said in ERROR_THREAD_EXISTS:
The remaining problem for C++ is that it's a library-thing and not a first-class citizen of the language. So while conceptually that is exactly what void is, it (and all equivalent empty structs of different name) don't get the same treatment.
See last paragraph above. Any implementation of a real unit type - in library, in language, whatever - would either require extensive changes across most of the 1836 pages of the C++ standard so no memory needs to be allocated for it, or allocate at least 1 byte for each instance of that unit type.
I’m not sure it’s that hard, or at least the people who proposed “regular void” said it would actually make the standard simpler instead of even more complex. But apparently it wasn’t accepted, so I have no idea why it didn’t work out, maybe it did fail some complex backwards compatibility edge case.
I think that the “no memory” part isn’t that impossible to solve. There’s
[[no_unique_address]]
now, the compiler could just know that void is empty or discard it under “as if” most of the time, etc. Mostly it would only crop up in new template code of the formauto x = f()
for auto=void that wasn’t even valid before. The few times you actually need an address of x and can’t eliminate it, that’ll be in code that didn’t even compile before, so you don’t lose anything (except for a word of stack space)But of course you’re right that this would be much easier to fix with a time machine.
-
@topspin said in ERROR_THREAD_EXISTS:
@Gąska said in ERROR_THREAD_EXISTS:
@topspin said in ERROR_THREAD_EXISTS:
The remaining problem for C++ is that it's a library-thing and not a first-class citizen of the language. So while conceptually that is exactly what void is, it (and all equivalent empty structs of different name) don't get the same treatment.
See last paragraph above. Any implementation of a real unit type - in library, in language, whatever - would either require extensive changes across most of the 1836 pages of the C++ standard so no memory needs to be allocated for it, or allocate at least 1 byte for each instance of that unit type.
I’m not sure it’s that hard, or at least the people who proposed “regular void” said it would actually make the standard simpler instead of even more complex. But apparently it wasn’t accepted, so I have no idea why it didn’t work out, maybe it did fail some complex backwards compatibility edge case.
Or maybe because they ignored the size issue entirely.
Why Isn't sizeof(void) Equal to 0?
One suggestion that has repeatedly come up is to have sizeof(void) report 0 and to allow multiple instances to share the same address. This would prevent users from having to use tricks akin to the empty base optimization in order to make more optimal usage of memory. Ideally, this would be the case, however such a change to the language is both vast and out of scope of the proposal. Allowing a type to have a 0 size and to allow separate instances to share an address implies drastic and subtle breaking changes to existing code. For instance, if you were to make an array of such a void type, a pointer, at least in the traditional sense, would no longer be able to be used as an iterator into that array (notably meaning that generic code which relies on this would now fail for such a size 0 type). As well, any code that relies on an object's type and address as unique would fail for void types, even though it is otherwise perfectly acceptable. Finally, if such a size were permitted for void, it should really be allowed for any type, including user-defined types. Having a special rule for void would make one more thing to have to think about and deal with differently for void types. Instead, this proposal opts to leave the size of void unspecified and thereby governed by existing language rules. In practice, it is expected that void will likely be size 1 in most implementations, though this is not required. If an eventual change were made to the language to allow for size 0 types, then void would be able to implicitly take advantage of that.
But if void size is non-zero, I suspect many void-related optimizations might not kick in, or require significant redesign of optimizers so they correctly elide allocation when they can but still keep this single byte around when they must.
-
@Gąska said in ERROR_THREAD_EXISTS:
@topspin said in ERROR_THREAD_EXISTS:
@Gąska said in ERROR_THREAD_EXISTS:
@topspin said in ERROR_THREAD_EXISTS:
The remaining problem for C++ is that it's a library-thing and not a first-class citizen of the language. So while conceptually that is exactly what void is, it (and all equivalent empty structs of different name) don't get the same treatment.
See last paragraph above. Any implementation of a real unit type - in library, in language, whatever - would either require extensive changes across most of the 1836 pages of the C++ standard so no memory needs to be allocated for it, or allocate at least 1 byte for each instance of that unit type.
I’m not sure it’s that hard, or at least the people who proposed “regular void” said it would actually make the standard simpler instead of even more complex. But apparently it wasn’t accepted, so I have no idea why it didn’t work out, maybe it did fail some complex backwards compatibility edge case.
Looks like it, I didn't remember the details. (Although it's arguably not "ignored" but "argued against")
For instance, if you were to make an array of such a void type, a pointer, at least in the traditional sense, would no longer be able to be used as an iterator into that array (notably meaning that generic code which relies on this would now fail for such a size 0 type).
Sounds convincing.
But if void size is non-zero, I suspect many void-related optimizations might not kick in, or require significant redesign of optimizers so they correctly elide allocation when they can but still keep this single byte around when they must.
Maybe. I think most optimizers would easily be able to treat it the same way it is currently, but that's just speculation.
-
@levicki said in ERROR_THREAD_EXISTS:
But can you even have an array of
void
(notvoid *
mind you) right now?It's about the future code that could be written with the new
void
rules, breaking up in all sorts of "fun" ways.Still waiting for that template syntax of yours BTW.
-
@levicki said in ERROR_THREAD_EXISTS:
@Gąska said in ERROR_THREAD_EXISTS:
But it makes life MUCH easier in certain situations.
Which are rare as hen's teeth, not to mention that doing functional programming in procedural language is in itself.
"I don't understand it therefore it's dumb and useless."
-
@levicki said in ERROR_THREAD_EXISTS:
@Gąska said in ERROR_THREAD_EXISTS:
It's about the future code that could be written with the new void rules
I am not sure what new code would be possible to write with
sizeof(void)
returning zero?That's the point - none without significant changes to the rest of the language (semantically, not syntactically). And if
sizeof(void)
isn't 0, returning from a void function becomes slightly slower without some very aggressive optimizations - and void functions are used often enough that it becomes a major problem."I don't understand it therefore it's dumb and useless."
I didn't say any of that. I said there are functional languages better suited for functional programming
You said way more than that. And you know it. You're just playing dumb now. Typical @levicki. "When I said it's WTF, I didn't mean it's WTF WTF."
Or you actually mean that using functional programming idioms in a language not specifically designed for functional programming is wrong and should never be done. In which case, you really don't understand what functional programming is about. Might be a case of Dunning-Kruger effect, depending on how much you believe you do.
and that use cases for your claim are RARE.
Rarity is a function of ease. The harder something is the less it's done. Making it even harder on the basis that it's currently rarely used is just retarded. Use another argument.
See lambdas. It was technically possible to write ad-hoc stateful function-like objects in C++03. Nobody was doing that because it was disgustingly verbose. But lo and behold, they've since made it much easier and now everyone uses it. Imagine if they've used your argument against inclusion of lambdas into C++11.
You didn't prove otherwise so it follows that we can do well without
void
.We did "well" without
auto
too.Seriously. I gave you one use case which is arguably quite rare, but nevertheless would become INCREDIBLY PAINFUL without
void
. And removal ofvoid
would allow... saving five characters per function? That live outside the function body anyway so they don't actually clutter anything? You really think that's a worthwhile tradeoff?
-
@levicki said in ERROR_THREAD_EXISTS:
I don't find lambdas that appealing. Sure they can be useful but IMO they make code less readable.
There is nothing good about
auto
.You must be clinically insane. Or an alien from outer space. Which is the same thing for all intensive porpoises. You could've put a disclaimer somewhere. There's nothing to be gained from talking to an insane person. EOT on my side.
-
@levicki said in ERROR_THREAD_EXISTS:
Also why do you insist writing a template without void keyword would be impossible? It would just have different syntax.
What's the point? You're ok with the concept but not the syntax? Why? Are you allergic to the letter
v
?
-
@levicki I have an entire library of good uses. It's called
<algorithm>
. I just don't think there's any sense trying to convince a person provably incapable of changing their mind that they should change their mind. You've had 8 years to change your mind, but instead you keep repeating that it was all technically possible before lambdas, as if ergonomics don't matter. It's like trying to convince a schizophrenic that no, his family doesn't want him dead (BTDT, do not recommend).
-
@levicki and since we're calling each other on not delivering - I'm still waiting for that template syntax that works on both functions and voidless procedures.
-
I keep getting first upvote notifications for the same posts... I wonder if someone is running the Vote Balance Thread script on this topic.
-
This discussion about
void
and the function/procedure distinction reminds me of the WTF in Texas Instruments TI-89 BASIC, which was the source of my very ingrained bias about the function/procedure distinction:They defined a function as "Can return a value and is absolutely pure". Which meant there was absolutely no way in the language to code a logical unit that prompted the user for a value, accepted said value and returned it, because of course UI is impure.
-
@Medinoc said in ERROR_THREAD_EXISTS:
This discussion about
void
and the function/procedure distinction reminds me of the WTF in Texas Instruments TI-89 BASIC, which was the source of my very ingrained bias about the function/procedure distinction:They defined a function as "Can return a value and is absolutely pure". Which meant there was absolutely no way in the language to code a logical unit that prompted the user for a value, accepted said value and returned it, because of course UI is impure.
Good times....
-
@Medinoc said in ERROR_THREAD_EXISTS:
This discussion about
void
and the function/procedure distinction reminds me of the WTF in Texas Instruments TI-89 BASIC, which was the source of my very ingrained bias about the function/procedure distinction:They defined a function as "Can return a value and is absolutely pure". Which meant there was absolutely no way in the language to code a logical unit that prompted the user for a value, accepted said value and returned it, because of course UI is impure.
oooooh now that brings back memories..... futzing around on one of those instead of paying attention in calculus class.... programming ever more complicated games on it using that tiny keyboard... Snake, and asteroids, and even pong were all done. I tried to get some facsimile of DOOM E1M1 programmed on that thing, even if it ran like ass, but.... yeah that didn't happen.
-
@Gąska said in ERROR_THREAD_EXISTS:
I keep getting first upvote notifications for the same posts... I wonder if someone is running the Vote Balance Thread script on this topic.
You get one whenever it hits a total of 1, not when you get the first upvote (but you know that). Sounds like the order of readers up-/downvoting happens to be consistent enough to cause this.
-
@topspin said in ERROR_THREAD_EXISTS:
At least they don’t produce the kind of shit where a procedure is different from a function. (As seen in Pascal, Basic, Fortran)
They aren't different though. At least in Pascal. The keyword procedure = void function. It cannot return a value and will throw a compilation error if you try to return a value. Aside from that both functions and procedures can be used as function pointers in function/procedures that can take function pointers as parameters. Hopefully no one thinks there are any differences beyond that. I can't speak to Basic as I have not used it in decades and I have never used Fortran.
-
@Gąska said in ERROR_THREAD_EXISTS:
See lambdas. It was technically possible to write ad-hoc stateful function-like objects in C++03. Nobody was doing that because it was disgustingly verbose. But lo and behold, they've since made it much easier and now everyone uses it. Imagine if they've used your argument against inclusion of lambdas into C++
We did "well" without auto too.The last time I touched C++ it didn't have that stuff.
Is it tolerable now?
-
@levicki said in ERROR_THREAD_EXISTS:
auto I said I don't like it because it hides the type and you can't mouseover to see what it is or Ctrl+Click to go to type definition.
It sounds like you have a shitty IDE and you're blaming the language. Shit, you can get that level of support for JavaScript and it doesn't even have type declarations.
-
@error said in ERROR_THREAD_EXISTS:
@Gąska said in ERROR_THREAD_EXISTS:
See lambdas. It was technically possible to write ad-hoc stateful function-like objects in C++03. Nobody was doing that because it was disgustingly verbose. But lo and behold, they've since made it much easier and now everyone uses it. Imagine if they've used your argument against inclusion of lambdas into C++
We did "well" without auto too.The last time I touched C++ it died have that stuff.
E_SYNTAX_ERROR
Is it tolerable now?
It's no worse than the rest of the language. Since C++14, you can have auto arguments on lambdas, and that makes them actually usable for
std::find_if
etc. C++20 will make them even better with ranges, because you won't have to provide begin and end iterators separately anymore.
-
@error said in ERROR_THREAD_EXISTS:
The last time I touched C++ it didn't have that stuff.
Is it tolerable now?
Well,
auto
is nice but the rest... is a very mixed bag.
-
@dkf said in ERROR_THREAD_EXISTS:
@error said in ERROR_THREAD_EXISTS:
The last time I touched C++ it didn't have that stuff.
Is it tolerable now?
Well,
auto
is nice but the rest... is a very mixed bag.C++11 upwards is way less painful than 98.
-
@Gąska said in ERROR_THREAD_EXISTS:
In which case, you really don't understand what functional programming is about. Might be a case of Dunning-Kruger effect, depending on how much you believe you do.
I'd argue it's more likely the Blub paradox.
-
@levicki said in ERROR_THREAD_EXISTS:
@boomzilla said in ERROR_THREAD_EXISTS:
I'd argue it's more likely the Blub paradox.
Or maybe it is just a feature creep in C++ without fixing some actual long-standing problems?
Maybe you have a point. Hmm... nah, you don't. It's not feature creep - it's adding something that was working very well in other languages and alleviates the biggest problem people had with the
_if
family of functions.We have SIMD 16-byte vectors in x86 since 1999 and it took until C++17 to get the aligned new operator shoehorned into the language
Which was only a problem if you totally ignore every other way you can already specify alignment, such as alignas specifier (which you should always use instead of aligned new anyway because not encoding alignment requirements in types is just asking for trouble).
-
@levicki said in ERROR_THREAD_EXISTS:
@Gąska alignas did not exist until C++11.
Exactly. 6 years before 17.
-
@levicki yes, C++98 was crap, and C++03 was only marginally better. Many features of C++11 were long overdue, and some of its ideas were so important it completely changed how C++ code is written (especially move semantics). But C++17 feels like it's just additional bells and whistles for the sake of having more bells and whistles - and aligned new is a prime example of that. It doesn't allow you to do anything new that couldn't be done before, it doesn't make code much shorter or any more clear, and it encourages design that blows the entire program up if you even as much as look at it in a wrong way.
-
Wow, that was a fast downvote. Is that how @Tsaukpaetra marks read posts in this topic?
-
@levicki C++17 added lambdas?
-
@Gąska said in ERROR_THREAD_EXISTS:
Wow, that was a fast downvote. Is that how @Tsaukpaetra marks read posts in this topic?
No. According to my read-tracker, my last-read is about 4 days ago.
-
@Tsaukpaetra sorry then!
Who else has so much determination and free time to go through every single post just to downvote it? I mean, it's 3 weeks and they're still at that.
-
@Gąska I could see how mentioning C++ would cause that.
-
@Gąska said in ERROR_THREAD_EXISTS:
@levicki yes, C++98 was crap, and C++03 was only marginally better. Many features of C++11 were long overdue, and some of its ideas were so important it completely changed how C++ code is written (especially move semantics). But C++17 feels like it's just additional bells and whistles for the sake of having more bells and whistles - and aligned new is a prime example of that. It doesn't allow you to do anything new that couldn't be done before, it doesn't make code much shorter or any more clear, and it encourages design that blows the entire program up if you even as much as look at it in a wrong way.
Just looked it up and from what I’ve read it seems
alignas
did not get respect bynew
before 17.
Sounds more like a defect to me, but
-
@levicki if you haven't noticed, it did.
-
@levicki said in ERROR_THREAD_EXISTS:
@Gąska said in ERROR_THREAD_EXISTS:
@levicki if you haven't noticed, it did.
No, I haven't noticed, I have noticed you said this:
After which @topspin made his post, after which I stopped saying the wrong thing. I meant to post something that would make it clear but forgot.
-
@levicki said in ERROR_THREAD_EXISTS:
@Gąska I had not doubts you will find an excuse for yourself
It's not an excuse. It's just getting the timeline straight. I said something. You said something that didn't contradict what I said. I kept saying my thing because why would I stop if there wasn't any sign it might be wrong. Then @topspin finally said something that contradicted my thing, and I stopped saying that thing because obviously it's wrong.
but not apologize for saying I was wrong when I wasn't.
I would apologize if you stopped spewing lies about me. You don't deserve to hear an apology even if I did wrong (and I did wrong, yes).
-
@levicki said in ERROR_THREAD_EXISTS:
@Gąska said in ERROR_THREAD_EXISTS:
I would apologize if you stopped spewing lies about me.
What lies?
That I keep saying the wrong thing after I already stopped, for one. Or that I do it all the time. Both are lies.
You don't deserve to hear an apology even if I did wrong...
Ah, the classic "Look what you made me do" argument.
Ah, the classic "look at this thing he didn't actually say but I prefer to imagine that's what he said" argument. No, you didn't make me do anything. I choose to not apologize to you because you're an incredible asshole.
-
@levicki said in ERROR_THREAD_EXISTS:
You claimed that there was a way to specify alignment for dynamic allocations before aligned new existed.
And you could correct me right there and then. But nooooo. Instead you started talking about all the things EXCEPT the one thing that mattered. If you knew alignas is ignored by dynamic allocation, and you've seen me saying that alignas is good enough for dynamic allocation, why didn't you say anything? Why did I have to wait for @topspin to accidentally stumble upon the relevant information to learn what wrong assumption I made?
Yes, I fucked up. Hard. But you still won't see an apology because you're just such an asshole.