Feature request: https://github.com/php/php-src/issues/13301

php.internals

Григорий Senior PHP / Разработчик Web

2 years ago
Hello, please discuss about error collecting implementation in next PHP releases Exceptions have common differences that restrict using them to collect errors 1. Timeloss (trace collection) on call new() on any class that implements \Throwable 2. To collect errors and return it to the upper level you have to change the return signature, so most of the time rewrite the full tree and change all signatures, that's inapplicable and causes more refactor time without any benefits 3. Exceptions will break code, so you need to catch them as much closely as possible to place you throw it (additional refactoring) What I want from this feature: - while I am writing the api, I need not only "log" the errors, but be able to send all script errors to json output, including warnings, errors, and deep nested. It will reduce the time of debugging, otherwise i have to download log files from server or configure external system like sentry that collects all errors by groups/chains Suggested solution: - add non-breakable interface and language construct `raise` to "throw" error without collecting trace - that error could be any scalar or object, or you can implement new interface for them, keeping that is nested and taggable array - this `raise` could be catched same way as \Throwable allowing log it anywhere you need or re-`raise` again - `raise` statement won't start to collapse/break code, so it could be skipped without affecting application Current solution: a) 2 classes - ErrorBag/ErrorBagStack. b) native functions like _error()/_warning() that stores errors to the current ErrorBag if it exists. c) ErrorBag exists only if you initialize it: - from the upper level (you need to collect) - directly inside a function (you need to decide return/continue depending on its emptiness) - otherwise _error()/_warning() does nothing d) once you "catch" results of _error()/_warning() you often need it to merge the result to the current errorbag, mark it with a nesting group or with the tag. Nesting group is required once you debug code (you will see the log), tags will needed once you want to copy results from one bag to another (closest example - reduce queue with unique function, do action, then work with initial data) e) useful feature is merge_as_warning($errorBag) - to prevent the current error bag to return `true` on call ->hasErrors(). Errors are related to low level function, and possibly you already do actions that allow you just store them. f) searching inside the error bag by nesting sequence, or by tag, or by error type. Error type helps the same as try/catch, tag helps if you want to save errors to several destinations without memory losses, and nesting will help most of the time in debugging. Thanks for your attention.
-- +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram 6562680@gmail.com

Arvids Godjuks

2 years ago
On Tue, 6 Feb 2024 at 15:58, Григорий Senior PHP / Разработчик Web < 6562680@gmail.com> wrote:
> Hello, please discuss about error collecting implementation in next PHP > releases > > Exceptions have common differences that restrict using them to collect > errors > 1. Timeloss (trace collection) on call new() on any class that implements > \Throwable > 2. To collect errors and return it to the upper level you have to change > the return signature, so most of the time rewrite the full tree and change > all signatures, that's inapplicable and causes more refactor time without > any benefits > 3. Exceptions will break code, so you need to catch them as much closely as > possible to place you throw it (additional refactoring) > > What I want from this feature: > - while I am writing the api, I need not only "log" the errors, but be able > to send all script errors to json output, including warnings, errors, and > deep nested. It will reduce the time of debugging, otherwise i have to > download log files from server or configure external system like sentry > that collects all errors by groups/chains > > Suggested solution: > - add non-breakable interface and language construct `raise` to "throw" > error without collecting trace > - that error could be any scalar or object, or you can implement new > interface for them, keeping that is nested and taggable array > - this `raise` could be catched same way as \Throwable allowing log it > anywhere you need or re-`raise` again > - `raise` statement won't start to collapse/break code, so it could be > skipped without affecting application > > Current solution: > a) 2 classes - ErrorBag/ErrorBagStack. > > b) native functions like _error()/_warning() that stores errors to the > current ErrorBag if it exists. > > c) ErrorBag exists only if you initialize it: > - from the upper level (you need to collect) > - directly inside a function (you need to decide return/continue depending > on its emptiness) > - otherwise _error()/_warning() does nothing > > d) once you "catch" results of _error()/_warning() you often need it to > merge the result to the current errorbag, mark it with a nesting group or > with the tag. Nesting group is required once you debug code (you will see > the log), tags will needed once you want to copy results from one bag to > another (closest example - reduce queue with unique function, do action, > then work with initial data) > > e) useful feature is merge_as_warning($errorBag) - to prevent the current > error bag to return `true` on call ->hasErrors(). Errors are related to low > level function, and possibly you already do actions that allow you just > store them. > > f) searching inside the error bag by nesting sequence, or by tag, or by > error type. Error type helps the same as try/catch, tag helps if you want > to save errors to several destinations without memory losses, and nesting > will help most of the time in debugging. > > Thanks for your attention. > > -- > +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи > https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram > 6562680@gmail.com >
Hello, This is an application design-level issue, not a language issue. All you need to do is implement a collector on the logger you use that will store the info you want and let you ask for that info just before you push data into the JSON encoder you use from that logger collector. It's as simple as that, you don't even need to change your existing code if it already logs the information.
-- Arvīds Godjuks +371 26 851 664 arvids.godjuks@gmail.com Telegram: @psihius https://t.me/psihius

Arvids Godjuks

2 years ago
On Tue, 6 Feb 2024 at 16:39, Arvids Godjuks <arvids.godjuks@gmail.com> wrote:
> > > On Tue, 6 Feb 2024 at 15:58, Григорий Senior PHP / Разработчик Web < > 6562680@gmail.com> wrote: > >> Hello, please discuss about error collecting implementation in next PHP >> releases >> >> Exceptions have common differences that restrict using them to collect >> errors >> 1. Timeloss (trace collection) on call new() on any class that implements >> \Throwable >> 2. To collect errors and return it to the upper level you have to change >> the return signature, so most of the time rewrite the full tree and change >> all signatures, that's inapplicable and causes more refactor time without >> any benefits >> 3. Exceptions will break code, so you need to catch them as much closely >> as >> possible to place you throw it (additional refactoring) >> >> What I want from this feature: >> - while I am writing the api, I need not only "log" the errors, but be >> able >> to send all script errors to json output, including warnings, errors, and >> deep nested. It will reduce the time of debugging, otherwise i have to >> download log files from server or configure external system like sentry >> that collects all errors by groups/chains >> >> Suggested solution: >> - add non-breakable interface and language construct `raise` to "throw" >> error without collecting trace >> - that error could be any scalar or object, or you can implement new >> interface for them, keeping that is nested and taggable array >> - this `raise` could be catched same way as \Throwable allowing log it >> anywhere you need or re-`raise` again >> - `raise` statement won't start to collapse/break code, so it could be >> skipped without affecting application >> >> Current solution: >> a) 2 classes - ErrorBag/ErrorBagStack. >> >> b) native functions like _error()/_warning() that stores errors to the >> current ErrorBag if it exists. >> >> c) ErrorBag exists only if you initialize it: >> - from the upper level (you need to collect) >> - directly inside a function (you need to decide return/continue depending >> on its emptiness) >> - otherwise _error()/_warning() does nothing >> >> d) once you "catch" results of _error()/_warning() you often need it to >> merge the result to the current errorbag, mark it with a nesting group or >> with the tag. Nesting group is required once you debug code (you will see >> the log), tags will needed once you want to copy results from one bag to >> another (closest example - reduce queue with unique function, do action, >> then work with initial data) >> >> e) useful feature is merge_as_warning($errorBag) - to prevent the current >> error bag to return `true` on call ->hasErrors(). Errors are related to >> low >> level function, and possibly you already do actions that allow you just >> store them. >> >> f) searching inside the error bag by nesting sequence, or by tag, or by >> error type. Error type helps the same as try/catch, tag helps if you want >> to save errors to several destinations without memory losses, and nesting >> will help most of the time in debugging. >> >> Thanks for your attention. >> >> -- >> +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи >> https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram >> 6562680@gmail.com >> > > Hello, > > This is an application design-level issue, not a language issue. > All you need to do is implement a collector on the logger you use that > will store the info you want and let you ask for that info just before you > push data into the JSON encoder you use from that logger collector. It's as > simple as that, you don't even need to change your existing code if it > already logs the information. > > -- > > Arvīds Godjuks > +371 26 851 664 > arvids.godjuks@gmail.com > Telegram: @psihius https://t.me/psihius >
Sending me multiple emails in private with rants is not a behaviour that's encouraged on this list. Please read the https://wiki.php.net/email_etiquette_for_people_new_to_php_internals
-- Arvīds Godjuks +371 26 851 664 arvids.godjuks@gmail.com Telegram: @psihius https://t.me/psihius

Григорий Senior PHP / Разработчик Web

2 years ago
Sending you private emails made because "answer" button in Gmail selects only you to receive. Sending you private emails that don't even read signs to me you don't need my answers and have no benefits from reading. But you deny, dont even want to understand. And notify all subscribers about what you don't want to understand, and about my mistakes. ``` It's business-level, capitalism, if you want so, issue. And it sounds like "we won't pay you for your mistakes". There's no magic pill that accidentally forces business to understand you now need errors collection and it needs at least two more days for "design-level" refactoring without business benefit. Also no magic pills to force the business owner to give you that 2 days before you face that requirement (you know, deadlines stuff). Refactoring should be reduced by time as much as possible otherwise you feel more stress on your job. I have no goal to convince you that this is necessary, I solved this problem for myself. But at the language level it can be solved more conveniently and better, and you will also benefit from this. All languages work as a stack. So errors could be collected as a stack on language level. Directly in place you need that stack instead of everywhere. Also forcing to close applications is safe-shield, instead of validation errors that could be found anywhere. PHP devs in most even imagined a solution that "you have to do validation as much earlier as you can". But... remote api responses still become from inside to outside, remote api requirements that accidentally arrive still become once your code is ready, and unpredictable. That simple enhancement allows you to implement error collection and dont touch your methods and signatures. Nice and easy. Or you can believe in `truth` and deny ideas because it does not correlate with your principles. ``` вт, 6 февр. 2024 г. в 17:56, Arvids Godjuks <arvids.godjuks@gmail.com>:
> > > On Tue, 6 Feb 2024 at 16:39, Arvids Godjuks <arvids.godjuks@gmail.com> > wrote: > >> >> >> On Tue, 6 Feb 2024 at 15:58, Григорий Senior PHP / Разработчик Web < >> 6562680@gmail.com> wrote: >> >>> Hello, please discuss about error collecting implementation in next PHP >>> releases >>> >>> Exceptions have common differences that restrict using them to collect >>> errors >>> 1. Timeloss (trace collection) on call new() on any class that implements >>> \Throwable >>> 2. To collect errors and return it to the upper level you have to change >>> the return signature, so most of the time rewrite the full tree and >>> change >>> all signatures, that's inapplicable and causes more refactor time without >>> any benefits >>> 3. Exceptions will break code, so you need to catch them as much closely >>> as >>> possible to place you throw it (additional refactoring) >>> >>> What I want from this feature: >>> - while I am writing the api, I need not only "log" the errors, but be >>> able >>> to send all script errors to json output, including warnings, errors, and >>> deep nested. It will reduce the time of debugging, otherwise i have to >>> download log files from server or configure external system like sentry >>> that collects all errors by groups/chains >>> >>> Suggested solution: >>> - add non-breakable interface and language construct `raise` to "throw" >>> error without collecting trace >>> - that error could be any scalar or object, or you can implement new >>> interface for them, keeping that is nested and taggable array >>> - this `raise` could be catched same way as \Throwable allowing log it >>> anywhere you need or re-`raise` again >>> - `raise` statement won't start to collapse/break code, so it could be >>> skipped without affecting application >>> >>> Current solution: >>> a) 2 classes - ErrorBag/ErrorBagStack. >>> >>> b) native functions like _error()/_warning() that stores errors to the >>> current ErrorBag if it exists. >>> >>> c) ErrorBag exists only if you initialize it: >>> - from the upper level (you need to collect) >>> - directly inside a function (you need to decide return/continue >>> depending >>> on its emptiness) >>> - otherwise _error()/_warning() does nothing >>> >>> d) once you "catch" results of _error()/_warning() you often need it to >>> merge the result to the current errorbag, mark it with a nesting group or >>> with the tag. Nesting group is required once you debug code (you will see >>> the log), tags will needed once you want to copy results from one bag to >>> another (closest example - reduce queue with unique function, do action, >>> then work with initial data) >>> >>> e) useful feature is merge_as_warning($errorBag) - to prevent the current >>> error bag to return `true` on call ->hasErrors(). Errors are related to >>> low >>> level function, and possibly you already do actions that allow you just >>> store them. >>> >>> f) searching inside the error bag by nesting sequence, or by tag, or by >>> error type. Error type helps the same as try/catch, tag helps if you want >>> to save errors to several destinations without memory losses, and nesting >>> will help most of the time in debugging. >>> >>> Thanks for your attention. >>> >>> -- >>> +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ >>> связи >>> https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram >>> 6562680@gmail.com >>> >> >> Hello, >> >> This is an application design-level issue, not a language issue. >> All you need to do is implement a collector on the logger you use that >> will store the info you want and let you ask for that info just before you >> push data into the JSON encoder you use from that logger collector. It's as >> simple as that, you don't even need to change your existing code if it >> already logs the information. >> >> -- >> >> Arvīds Godjuks >> +371 26 851 664 >> arvids.godjuks@gmail.com >> Telegram: @psihius https://t.me/psihius >> > > Sending me multiple emails in private with rants is not a behaviour that's > encouraged on this list. Please read the > https://wiki.php.net/email_etiquette_for_people_new_to_php_internals > -- > > Arvīds Godjuks > +371 26 851 664 > arvids.godjuks@gmail.com > Telegram: @psihius https://t.me/psihius >
-- +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram 6562680@gmail.com

Alexander Pravdin

2 years ago
On Wed, Feb 7, 2024 at 12:00 AM Григорий Senior PHP / Разработчик Web <6562680@gmail.com> wrote:
> > Sending you private emails made because "answer" button in Gmail selects > only you to receive. > Sending you private emails that don't even read signs to me you don't need > my answers and have no benefits from reading. But you deny, dont even want > to understand. And notify all subscribers about what you don't want to > understand, and about my mistakes. > > ``` > It's business-level, capitalism, if you want so, issue. And it sounds like > "we won't pay you for your mistakes". > > There's no magic pill that accidentally forces business to understand you > now need errors collection and it needs at least two more days for > "design-level" refactoring without business benefit. Also no magic pills to > force the business owner to give you that 2 days before you face that > requirement (you know, deadlines stuff). > > Refactoring should be reduced by time as much as possible otherwise you > feel more stress on your job. I have no goal to convince you that this is > necessary, I solved this problem for myself. But at the language level it > can be solved more conveniently and better, and you will also benefit from > this. > > All languages work as a stack. So errors could be collected as a stack on > language level. Directly in place you need that stack instead of > everywhere. Also forcing to close applications is safe-shield, instead of > validation errors that could be found anywhere. PHP devs in most even > imagined a solution that "you have to do validation as much earlier as you > can". But... remote api responses still become from inside to outside, > remote api requirements that accidentally arrive still become once your > code is ready, and unpredictable. > > That simple enhancement allows you to implement error collection and > dont touch your methods and signatures. Nice and easy. Or you can believe > in `truth` and deny ideas because it does not correlate with your > principles.
Hey friend, easy :) You was correctly pointed out that this is an application-level issue, not a language one. It is your responsibility to collect anything you want and deal with the business when it puts some requirements. Did you ever experience out-of-memory issues because something in your application is collecting some stuff and don't dispose it? Did you ever work with algorithms running iterations over millions of records in PHP? Can you imagine a language-level bomb if PHP will collect anything in its own memory in every iteration of this kind and not dispose it during the whole request? You don't need to reply to me, just take a deep breath and think about it. I believe this kind of discussions is an off-topic in this mailing list.
-- Best, Alexander.

Григорий Senior PHP / Разработчик Web

2 years ago
Short answer is yes. Glad to see that personally adapted answer. That's why in the relevant github issue i show how to collect ONLY if you need. If you initialize the error bag - it collects, if not - it skips. T So the `try/catch` statement outside means you initialized, also a special decorator or additional command could initiate the raise collector right in the function ! You collect only the cases you want to collect. That's the difference. вт, 6 февр. 2024 г. в 18:20, Alexander Pravdin <alex.pravdin@interi.co>:
> On Wed, Feb 7, 2024 at 12:00 AM Григорий Senior PHP / Разработчик Web > <6562680@gmail.com> wrote: > > > > Sending you private emails made because "answer" button in Gmail selects > > only you to receive. > > Sending you private emails that don't even read signs to me you don't > need > > my answers and have no benefits from reading. But you deny, dont even > want > > to understand. And notify all subscribers about what you don't want to > > understand, and about my mistakes. > > > > ``` > > It's business-level, capitalism, if you want so, issue. And it sounds > like > > "we won't pay you for your mistakes". > > > > There's no magic pill that accidentally forces business to understand you > > now need errors collection and it needs at least two more days for > > "design-level" refactoring without business benefit. Also no magic pills > to > > force the business owner to give you that 2 days before you face that > > requirement (you know, deadlines stuff). > > > > Refactoring should be reduced by time as much as possible otherwise you > > feel more stress on your job. I have no goal to convince you that this is > > necessary, I solved this problem for myself. But at the language level it > > can be solved more conveniently and better, and you will also benefit > from > > this. > > > > All languages work as a stack. So errors could be collected as a stack on > > language level. Directly in place you need that stack instead of > > everywhere. Also forcing to close applications is safe-shield, instead of > > validation errors that could be found anywhere. PHP devs in most even > > imagined a solution that "you have to do validation as much earlier as > you > > can". But... remote api responses still become from inside to outside, > > remote api requirements that accidentally arrive still become once your > > code is ready, and unpredictable. > > > > That simple enhancement allows you to implement error collection and > > dont touch your methods and signatures. Nice and easy. Or you can believe > > in `truth` and deny ideas because it does not correlate with your > > principles. > > Hey friend, easy :) > > You was correctly pointed out that this is an application-level issue, > not a language one. It is your responsibility to collect anything you > want and deal with the business when it puts some requirements. Did > you ever experience out-of-memory issues because something in your > application is collecting some stuff and don't dispose it? Did you > ever work with algorithms running iterations over millions of records > in PHP? Can you imagine a language-level bomb if PHP will collect > anything in its own memory in every iteration of this kind and not > dispose it during the whole request? > > You don't need to reply to me, just take a deep breath and think about > it. I believe this kind of discussions is an off-topic in this mailing > list. > > > -- > Best, Alexander. > > > > ``` > > > > вт, 6 февр. 2024 г. в 17:56, Arvids Godjuks <arvids.godjuks@gmail.com>: > > > > > > > > > > > On Tue, 6 Feb 2024 at 16:39, Arvids Godjuks <arvids.godjuks@gmail.com> > > > wrote: > > > > > >> > > >> > > >> On Tue, 6 Feb 2024 at 15:58, Григорий Senior PHP / Разработчик Web < > > >> 6562680@gmail.com> wrote: > > >> > > >>> Hello, please discuss about error collecting implementation in next > PHP > > >>> releases > > >>> > > >>> Exceptions have common differences that restrict using them to > collect > > >>> errors > > >>> 1. Timeloss (trace collection) on call new() on any class that > implements > > >>> \Throwable > > >>> 2. To collect errors and return it to the upper level you have to > change > > >>> the return signature, so most of the time rewrite the full tree and > > >>> change > > >>> all signatures, that's inapplicable and causes more refactor time > without > > >>> any benefits > > >>> 3. Exceptions will break code, so you need to catch them as much > closely > > >>> as > > >>> possible to place you throw it (additional refactoring) > > >>> > > >>> What I want from this feature: > > >>> - while I am writing the api, I need not only "log" the errors, but > be > > >>> able > > >>> to send all script errors to json output, including warnings, > errors, and > > >>> deep nested. It will reduce the time of debugging, otherwise i have > to > > >>> download log files from server or configure external system like > sentry > > >>> that collects all errors by groups/chains > > >>> > > >>> Suggested solution: > > >>> - add non-breakable interface and language construct `raise` to > "throw" > > >>> error without collecting trace > > >>> - that error could be any scalar or object, or you can implement new > > >>> interface for them, keeping that is nested and taggable array > > >>> - this `raise` could be catched same way as \Throwable allowing log > it > > >>> anywhere you need or re-`raise` again > > >>> - `raise` statement won't start to collapse/break code, so it could > be > > >>> skipped without affecting application > > >>> > > >>> Current solution: > > >>> a) 2 classes - ErrorBag/ErrorBagStack. > > >>> > > >>> b) native functions like _error()/_warning() that stores errors to > the > > >>> current ErrorBag if it exists. > > >>> > > >>> c) ErrorBag exists only if you initialize it: > > >>> - from the upper level (you need to collect) > > >>> - directly inside a function (you need to decide return/continue > > >>> depending > > >>> on its emptiness) > > >>> - otherwise _error()/_warning() does nothing > > >>> > > >>> d) once you "catch" results of _error()/_warning() you often need it > to > > >>> merge the result to the current errorbag, mark it with a nesting > group or > > >>> with the tag. Nesting group is required once you debug code (you > will see > > >>> the log), tags will needed once you want to copy results from one > bag to > > >>> another (closest example - reduce queue with unique function, do > action, > > >>> then work with initial data) > > >>> > > >>> e) useful feature is merge_as_warning($errorBag) - to prevent the > current > > >>> error bag to return `true` on call ->hasErrors(). Errors are related > to > > >>> low > > >>> level function, and possibly you already do actions that allow you > just > > >>> store them. > > >>> > > >>> f) searching inside the error bag by nesting sequence, or by tag, or > by > > >>> error type. Error type helps the same as try/catch, tag helps if you > want > > >>> to save errors to several destinations without memory losses, and > nesting > > >>> will help most of the time in debugging. > > >>> > > >>> Thanks for your attention. > > >>> > > >>> -- > > >>> +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ > > >>> связи > > >>> https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram > > >>> 6562680@gmail.com > > >>> > > >> > > >> Hello, > > >> > > >> This is an application design-level issue, not a language issue. > > >> All you need to do is implement a collector on the logger you use that > > >> will store the info you want and let you ask for that info just > before you > > >> push data into the JSON encoder you use from that logger collector. > It's as > > >> simple as that, you don't even need to change your existing code if it > > >> already logs the information. > > >> > > >> -- > > >> > > >> Arvīds Godjuks > > >> +371 26 851 664 > > >> arvids.godjuks@gmail.com > > >> Telegram: @psihius https://t.me/psihius > > >> > > > > > > Sending me multiple emails in private with rants is not a behaviour > that's > > > encouraged on this list. Please read the > > > https://wiki.php.net/email_etiquette_for_people_new_to_php_internals > > > -- > > > > > > Arvīds Godjuks > > > +371 26 851 664 > > > arvids.godjuks@gmail.com > > > Telegram: @psihius https://t.me/psihius > > > > > > > > > -- > > +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ > связи > > https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram > > 6562680@gmail.com >
-- +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram 6562680@gmail.com

Alex Wells

2 years ago
On Tue, Feb 6, 2024 at 5:26 PM Григорий Senior PHP / Разработчик Web < 6562680@gmail.com> wrote:
> Short answer is yes. Glad to see that personally adapted answer. >
What are those languages specifically?

Robert Landers

2 years ago
On Tue, Feb 6, 2024 at 4:26 PM Григорий Senior PHP / Разработчик Web <6562680@gmail.com> wrote:
> > Short answer is yes. Glad to see that personally adapted answer. > > That's why in the relevant github issue i show how to collect ONLY if you > need. > If you initialize the error bag - it collects, if not - it skips. T > > So the `try/catch` statement outside means you initialized, also a special > decorator or additional command could initiate the raise collector right in > the function ! > > You collect only the cases you want to collect. That's the difference. > > > > Tue, Feb 6 2024 at 18:20, Alexander Pravdin <alex.pravdin@interi.co>: > > > On Wed, Feb 7, 2024 at 12:00 AM Григорий Senior PHP / Разработчик Web > > <6562680@gmail.com> wrote: > > > > > > Sending you private emails made because "answer" button in Gmail selects > > > only you to receive. > > > Sending you private emails that don't even read signs to me you don't > > need > > > my answers and have no benefits from reading. But you deny, dont even > > want > > > to understand. And notify all subscribers about what you don't want to > > > understand, and about my mistakes. > > > > > > ``` > > > It's business-level, capitalism, if you want so, issue. And it sounds > > like > > > "we won't pay you for your mistakes". > > > > > > There's no magic pill that accidentally forces business to understand you > > > now need errors collection and it needs at least two more days for > > > "design-level" refactoring without business benefit. Also no magic pills > > to > > > force the business owner to give you that 2 days before you face that > > > requirement (you know, deadlines stuff). > > > > > > Refactoring should be reduced by time as much as possible otherwise you > > > feel more stress on your job. I have no goal to convince you that this is > > > necessary, I solved this problem for myself. But at the language level it > > > can be solved more conveniently and better, and you will also benefit > > from > > > this. > > > > > > All languages work as a stack. So errors could be collected as a stack on > > > language level. Directly in place you need that stack instead of > > > everywhere. Also forcing to close applications is safe-shield, instead of > > > validation errors that could be found anywhere. PHP devs in most even > > > imagined a solution that "you have to do validation as much earlier as > > you > > > can". But... remote api responses still become from inside to outside, > > > remote api requirements that accidentally arrive still become once your > > > code is ready, and unpredictable. > > > > > > That simple enhancement allows you to implement error collection and > > > dont touch your methods and signatures. Nice and easy. Or you can believe > > > in `truth` and deny ideas because it does not correlate with your > > > principles. > > > > Hey friend, easy :) > > > > You was correctly pointed out that this is an application-level issue, > > not a language one. It is your responsibility to collect anything you > > want and deal with the business when it puts some requirements. Did > > you ever experience out-of-memory issues because something in your > > application is collecting some stuff and don't dispose it? Did you > > ever work with algorithms running iterations over millions of records > > in PHP? Can you imagine a language-level bomb if PHP will collect > > anything in its own memory in every iteration of this kind and not > > dispose it during the whole request? > > > > You don't need to reply to me, just take a deep breath and think about > > it. I believe this kind of discussions is an off-topic in this mailing > > list. > > > > > > -- > > Best, Alexander. > > > > > > > ``` > > > > > > вт, 6 февр. 2024 г. в 17:56, Arvids Godjuks <arvids.godjuks@gmail.com>: > > > > > > > > > > > > > > > On Tue, 6 Feb 2024 at 16:39, Arvids Godjuks <arvids.godjuks@gmail.com> > > > > wrote: > > > > > > > >> > > > >> > > > >> On Tue, 6 Feb 2024 at 15:58, Григорий Senior PHP / Разработчик Web < > > > >> 6562680@gmail.com> wrote: > > > >> > > > >>> Hello, please discuss about error collecting implementation in next > > PHP > > > >>> releases > > > >>> > > > >>> Exceptions have common differences that restrict using them to > > collect > > > >>> errors > > > >>> 1. Timeloss (trace collection) on call new() on any class that > > implements > > > >>> \Throwable > > > >>> 2. To collect errors and return it to the upper level you have to > > change > > > >>> the return signature, so most of the time rewrite the full tree and > > > >>> change > > > >>> all signatures, that's inapplicable and causes more refactor time > > without > > > >>> any benefits > > > >>> 3. Exceptions will break code, so you need to catch them as much > > closely > > > >>> as > > > >>> possible to place you throw it (additional refactoring) > > > >>> > > > >>> What I want from this feature: > > > >>> - while I am writing the api, I need not only "log" the errors, but > > be > > > >>> able > > > >>> to send all script errors to json output, including warnings, > > errors, and > > > >>> deep nested. It will reduce the time of debugging, otherwise i have > > to > > > >>> download log files from server or configure external system like > > sentry > > > >>> that collects all errors by groups/chains > > > >>> > > > >>> Suggested solution: > > > >>> - add non-breakable interface and language construct `raise` to > > "throw" > > > >>> error without collecting trace > > > >>> - that error could be any scalar or object, or you can implement new > > > >>> interface for them, keeping that is nested and taggable array > > > >>> - this `raise` could be catched same way as \Throwable allowing log > > it > > > >>> anywhere you need or re-`raise` again > > > >>> - `raise` statement won't start to collapse/break code, so it could > > be > > > >>> skipped without affecting application > > > >>> > > > >>> Current solution: > > > >>> a) 2 classes - ErrorBag/ErrorBagStack. > > > >>> > > > >>> b) native functions like _error()/_warning() that stores errors to > > the > > > >>> current ErrorBag if it exists. > > > >>> > > > >>> c) ErrorBag exists only if you initialize it: > > > >>> - from the upper level (you need to collect) > > > >>> - directly inside a function (you need to decide return/continue > > > >>> depending > > > >>> on its emptiness) > > > >>> - otherwise _error()/_warning() does nothing > > > >>> > > > >>> d) once you "catch" results of _error()/_warning() you often need it > > to > > > >>> merge the result to the current errorbag, mark it with a nesting > > group or > > > >>> with the tag. Nesting group is required once you debug code (you > > will see > > > >>> the log), tags will needed once you want to copy results from one > > bag to > > > >>> another (closest example - reduce queue with unique function, do > > action, > > > >>> then work with initial data) > > > >>> > > > >>> e) useful feature is merge_as_warning($errorBag) - to prevent the > > current > > > >>> error bag to return `true` on call ->hasErrors(). Errors are related > > to > > > >>> low > > > >>> level function, and possibly you already do actions that allow you > > just > > > >>> store them. > > > >>> > > > >>> f) searching inside the error bag by nesting sequence, or by tag, or > > by > > > >>> error type. Error type helps the same as try/catch, tag helps if you > > want > > > >>> to save errors to several destinations without memory losses, and > > nesting > > > >>> will help most of the time in debugging. > > > >>> > > > >>> Thanks for your attention. > > > >>> > > > >>> -- > > > >>> +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ > > > >>> связи > > > >>> https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram > > > >>> 6562680@gmail.com > > > >>> > > > >> > > > >> Hello, > > > >> > > > >> This is an application design-level issue, not a language issue. > > > >> All you need to do is implement a collector on the logger you use that > > > >> will store the info you want and let you ask for that info just > > before you > > > >> push data into the JSON encoder you use from that logger collector. > > It's as > > > >> simple as that, you don't even need to change your existing code if it > > > >> already logs the information. > > > >> > > > >> -- > > > >> > > > >> Arvīds Godjuks > > > >> +371 26 851 664 > > > >> arvids.godjuks@gmail.com > > > >> Telegram: @psihius https://t.me/psihius > > > >> > > > > > > > > Sending me multiple emails in private with rants is not a behaviour > > that's > > > > encouraged on this list. Please read the > > > > https://wiki.php.net/email_etiquette_for_people_new_to_php_internals > > > > -- > > > > > > > > Arvīds Godjuks > > > > +371 26 851 664 > > > > arvids.godjuks@gmail.com > > > > Telegram: @psihius https://t.me/psihius > > > > > > > > > > > > > -- > > > +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ > > связи > > > https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram > > > 6562680@gmail.com > > > > > -- > +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи > https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram > 6562680@gmail.com
I recommend looking at how other applications handle this. For example, WordPress uses the WP_Error class to collect errors and returns ProperReturnType|WP_Error, and error handling is very similar to Go. Symfony validations simply collect validation errors and an empty array is "all good." Both approaches are totally fine, but it depends on what you are actually trying to accomplish. Robert Landers Software Engineer Utrecht NL

Alex Wells

2 years ago
On Tue, Feb 6, 2024 at 3:58 PM Григорий Senior PHP / Разработчик Web < 6562680@gmail.com> wrote:
> - add non-breakable interface and language construct `raise` to "throw" > error without collecting trace > - that error could be any scalar or object, or you can implement new > interface for them, keeping that is nested and taggable array > - this `raise` could be catched same way as \Throwable allowing log it > anywhere you need or re-`raise` again > - `raise` statement won't start to collapse/break code, so it could be > skipped without affecting application >
Is there an existing language that does that, having both exceptions and these silent raise statements?

Григорий Senior PHP / Разработчик Web

2 years ago
Javascript is closer to. It allows you to throw anything, but it is still the throw statement, keeping in the mind the async nature of js - memory and processor stuff is shared by the time. JS seniors usually hate those guys who throw anything except language Error class because they skipped the mandatory level of programming - OOP. They are now taking fun from it. We're tired of OOP for now. Once you work with batches/queue/bulks you need pipelines and chaining, and there's a throw works only to stop any certain tasks and almost immediately catch the next line. So `throw` is required to be safe-shield, but solves not enough count of cases. Old, maybe 10 years ago, Fowler's article about "errors is not an exception". He explained why, but recommend to implement own error bag. I tried few times implement own error bag on production ready code. And this is the hell of rewriting full nesting tree and carrying that return statement to upper level again and again, then you start to get confused, then you rewrite all return to objects with properties/getters/setters, then you understand your PHPStorm started to lag because of 70 uses in class... Better to use global error bag stack that you can enable or disable for your needs outside function ("in controller", GRASP) or inside function directly like old good times $errors[] and if ($errors) { return null; } вт, 6 февр. 2024 г. в 18:23, Alex Wells <autaut03@gmail.com>:
> On Tue, Feb 6, 2024 at 3:58 PM Григорий Senior PHP / Разработчик Web < > 6562680@gmail.com> wrote: > >> - add non-breakable interface and language construct `raise` to "throw" >> error without collecting trace >> - that error could be any scalar or object, or you can implement new >> interface for them, keeping that is nested and taggable array >> - this `raise` could be catched same way as \Throwable allowing log it >> anywhere you need or re-`raise` again >> - `raise` statement won't start to collapse/break code, so it could be >> skipped without affecting application >> > > Is there an existing language that does that, having both exceptions and > these silent raise statements? >
-- +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram 6562680@gmail.com

Arvids Godjuks

2 years ago
JavaScript is JavaScript - it's not a good role model to look at. If anything, JavaScript is a collection of things of how not to design a language :) What you are looking for is Golang. The level of changes you are proposing require it to go thriugh an RFC process, have 2/3rds of voters to agree to it. Tland that is after a feasibility study is even done - engine might not even allow to implement such thi g and require exte sive modifications for a thing that should be done on application level to begin with. And memory usage is one of the biggest points against it - engine allowing to store I side it arbitrary data that is logged by application on a per-request level is just a bad idea. People will shove megabytes of logs into it in a loop and them file reports "why is php using 2 GB of RAM?" - this is literally a daily question you get woth relation to Doctrine when people try to run bulk operations, do not disable trace logger and them run into the memory limit. What you are proposing is a footgun at it's finest and PHP has a rich history of those and we have learned from the experience as a community. Things like this are left to the userland. There are many libraries that help handle this. On Tue, Feb 6, 2024, 17:35 Григорий Senior PHP / Разработчик Web < 6562680@gmail.com> wrote:

Григорий Senior PHP / Разработчик Web

2 years ago
My function seems like this: ``` _error_bag_error(error) { if (stack.errorBag) { stack.errorBag.add(error); } } ``` It does nothing if i didn't initialize the error bag manually. I should call _error_bag() inside the current function to create one in the stack, or _error_bag_push() (and then _error_bag_pop()) outside the function to collect children. Doctrine's main problem is the dreadnought that throws low level exceptions and forces developers to spend weeks to understand "wow, that's the way it should be". Funny but painful. For one small benefit - reducing the count of queries by unique insertions/deletions, maybe 10% of queries are removed. вт, 6 февр. 2024 г. в 18:54, Arvids Godjuks <arvids.godjuks@gmail.com>:
> JavaScript is JavaScript - it's not a good role model to look at. If > anything, JavaScript is a collection of things of how not to design a > language :) > > What you are looking for is Golang. > > The level of changes you are proposing require it to go thriugh an RFC > process, have 2/3rds of voters to agree to it. Tland that is after a > feasibility study is even done - engine might not even allow to implement > such thi g and require exte sive modifications for a thing that should be > done on application level to begin with. > > And memory usage is one of the biggest points against it - engine allowing > to store I side it arbitrary data that is logged by application on a > per-request level is just a bad idea. People will shove megabytes of logs > into it in a loop and them file reports "why is php using 2 GB of RAM?" - > this is literally a daily question you get woth relation to Doctrine when > people try to run bulk operations, do not disable trace logger and them run > into the memory limit. > > > What you are proposing is a footgun at it's finest and PHP has a rich > history of those and we have learned from the experience as a community. > Things like this are left to the userland. There are many libraries that > help handle this. > > On Tue, Feb 6, 2024, 17:35 Григорий Senior PHP / Разработчик Web < > 6562680@gmail.com> wrote: > >> Javascript is closer to. >> >> It allows you to throw anything, but it is still the throw statement, >> keeping in the mind the async nature of js - memory and processor stuff is >> shared by the time. >> >> JS seniors usually hate those guys who throw anything except language >> Error >> class because they skipped the mandatory level of programming - OOP. They >> are now taking fun from it. We're tired of OOP for now. Once you work with >> batches/queue/bulks you need pipelines and chaining, and there's a throw >> works only to stop any certain tasks and almost immediately catch the next >> line. So `throw` is required to be safe-shield, but solves not enough >> count >> of cases. >> >> Old, maybe 10 years ago, Fowler's article about "errors is not an >> exception". He explained why, but recommend to implement own error bag. I >> tried few times implement own error bag on production ready code. And this >> is the hell of rewriting full nesting tree and carrying that return >> statement to upper level again and again, then you start to get confused, >> then you rewrite all return to objects with properties/getters/setters, >> then you understand your PHPStorm started to lag because of 70 uses in >> class... Better to use global error bag stack that you can enable or >> disable for your needs outside function ("in controller", GRASP) or inside >> function directly like old good times $errors[] and if ($errors) { return >> null; } >> >> >> вт, 6 февр. 2024 г. в 18:23, Alex Wells <autaut03@gmail.com>: >> >> > On Tue, Feb 6, 2024 at 3:58 PM Григорий Senior PHP / Разработчик Web < >> > 6562680@gmail.com> wrote: >> > >> >> - add non-breakable interface and language construct `raise` to "throw" >> >> error without collecting trace >> >> - that error could be any scalar or object, or you can implement new >> >> interface for them, keeping that is nested and taggable array >> >> - this `raise` could be catched same way as \Throwable allowing log it >> >> anywhere you need or re-`raise` again >> >> - `raise` statement won't start to collapse/break code, so it could be >> >> skipped without affecting application >> >> >> > >> > Is there an existing language that does that, having both exceptions and >> > these silent raise statements? >> > >> >> >> -- >> +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи >> https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram >> 6562680@gmail.com >> >
-- +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram 6562680@gmail.com

Григорий Senior PHP / Разработчик Web

2 years ago
Btw, i agree about Javascript, but on a low level it produces the most clean code, because there's no types and rules. All types moved to TypeScript's client side compiler. JS 15 years ago ACCIDENTALLY created a pipeline. Named it "Promise". We spent years after to understand that while (true) and then/catch should be different patterns. вт, 6 февр. 2024 г. в 19:08, Григорий Senior PHP / Разработчик Web < 6562680@gmail.com>:
> My function seems like this: > > ``` > _error_bag_error(error) { > if (stack.errorBag) { > stack.errorBag.add(error); > } > } > ``` > > It does nothing if i didn't initialize the error bag manually. > I should call _error_bag() inside the current function to create one in > the stack, or _error_bag_push() (and then _error_bag_pop()) outside the > function to collect children. > > Doctrine's main problem is the dreadnought that throws low level > exceptions and forces developers to spend weeks to understand "wow, that's > the way it should be". Funny but painful. For one small benefit - reducing > the count of queries by unique insertions/deletions, maybe 10% of queries > are removed. > > вт, 6 февр. 2024 г. в 18:54, Arvids Godjuks <arvids.godjuks@gmail.com>: > >> JavaScript is JavaScript - it's not a good role model to look at. If >> anything, JavaScript is a collection of things of how not to design a >> language :) >> >> What you are looking for is Golang. >> >> The level of changes you are proposing require it to go thriugh an RFC >> process, have 2/3rds of voters to agree to it. Tland that is after a >> feasibility study is even done - engine might not even allow to implement >> such thi g and require exte sive modifications for a thing that should be >> done on application level to begin with. >> >> And memory usage is one of the biggest points against it - engine >> allowing to store I side it arbitrary data that is logged by application on >> a per-request level is just a bad idea. People will shove megabytes of logs >> into it in a loop and them file reports "why is php using 2 GB of RAM?" - >> this is literally a daily question you get woth relation to Doctrine when >> people try to run bulk operations, do not disable trace logger and them run >> into the memory limit. >> >> >> What you are proposing is a footgun at it's finest and PHP has a rich >> history of those and we have learned from the experience as a community. >> Things like this are left to the userland. There are many libraries that >> help handle this. >> >> On Tue, Feb 6, 2024, 17:35 Григорий Senior PHP / Разработчик Web < >> 6562680@gmail.com> wrote: >> >>> Javascript is closer to. >>> >>> It allows you to throw anything, but it is still the throw statement, >>> keeping in the mind the async nature of js - memory and processor stuff >>> is >>> shared by the time. >>> >>> JS seniors usually hate those guys who throw anything except language >>> Error >>> class because they skipped the mandatory level of programming - OOP. They >>> are now taking fun from it. We're tired of OOP for now. Once you work >>> with >>> batches/queue/bulks you need pipelines and chaining, and there's a throw >>> works only to stop any certain tasks and almost immediately catch the >>> next >>> line. So `throw` is required to be safe-shield, but solves not enough >>> count >>> of cases. >>> >>> Old, maybe 10 years ago, Fowler's article about "errors is not an >>> exception". He explained why, but recommend to implement own error bag. I >>> tried few times implement own error bag on production ready code. And >>> this >>> is the hell of rewriting full nesting tree and carrying that return >>> statement to upper level again and again, then you start to get confused, >>> then you rewrite all return to objects with properties/getters/setters, >>> then you understand your PHPStorm started to lag because of 70 uses in >>> class... Better to use global error bag stack that you can enable or >>> disable for your needs outside function ("in controller", GRASP) or >>> inside >>> function directly like old good times $errors[] and if ($errors) { return >>> null; } >>> >>> >>> вт, 6 февр. 2024 г. в 18:23, Alex Wells <autaut03@gmail.com>: >>> >>> > On Tue, Feb 6, 2024 at 3:58 PM Григорий Senior PHP / Разработчик Web < >>> > 6562680@gmail.com> wrote: >>> > >>> >> - add non-breakable interface and language construct `raise` to >>> "throw" >>> >> error without collecting trace >>> >> - that error could be any scalar or object, or you can implement new >>> >> interface for them, keeping that is nested and taggable array >>> >> - this `raise` could be catched same way as \Throwable allowing log it >>> >> anywhere you need or re-`raise` again >>> >> - `raise` statement won't start to collapse/break code, so it could be >>> >> skipped without affecting application >>> >> >>> > >>> > Is there an existing language that does that, having both exceptions >>> and >>> > these silent raise statements? >>> > >>> >>> >>> -- >>> +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ >>> связи >>> https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram >>> 6562680@gmail.com >>> >> > > -- > +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи > https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram > 6562680@gmail.com >
-- +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram 6562680@gmail.com

Larry Garfield

2 years ago
On Tue, Feb 6, 2024, at 4:13 PM, Григорий Senior PHP / Разработчик Web wrote:
> Btw, i agree about Javascript, but on a low level it produces the most > clean code, because there's no types and rules. All types moved to > TypeScript's client side compiler. > > JS 15 years ago ACCIDENTALLY created a pipeline. Named it "Promise". We > spent years after to understand that while (true) and then/catch should be > different patterns.
I assume much of this thread is a language-barrier issue, which is making it more hostile than it needs to be. So let me try and expand a bit, because I am actually quite sympathetic to the OP's request, though not the way it's being made. First of all, please don't top post. It is considered rude on this list. GMail makes it a bit annoying to bottom post but it can be done. Please do so. Second, there's considerable prior art and discussion on the topic of error handling and exceptions. In particular, I recommend this excellent article by Joe Duffy: https://joeduffyblog.com/2016/02/07/the-error-model/ And this one from me: https://peakd.com/hive-168588/@crell/much-ado-about-null Yes, they're long, but error handling is a topic that requires more than casual thought. To summarize the articles for the short of time: * Exceptions in many languages (including PHP) are very expensive. The stack trace is one of the most expensive things PHP does. This is one of the reasons why exceptions are terrible for flow control. * Unchecked exceptions (where a function doesn't define what it can throw) are a great way to break your application in exciting and unpredictable ways. (This is the other reason exceptions are terrible for flow control.) * Many people find checked exceptions cumbersome, even though they are better (for reasons Duffy goes into). This is due mostly to bad design of checked exceptions in JVM languages. * The real problem is that we have a channel for a success case (return value), a channel for catastrophic failure (exceptions), but no channel for mundane errors (things a responsible developer should expect and know how to handle gracefully). So those get shoved into one or the other, usually an exception. The need is for a "mundane error" channel. I agree with this. Different languages handle it in different ways. * Go has multi-returns. * Rust has the Result type (which is an Either monad), and an auto-propagation operator (?). * PHP has union types (though that's not a deliberate design, just an emergent one). One of the key differences between different approaches is whether they force you to handle error cases (Result type) or let you ignore errors and assume a happy path (Go, PHP), letting an unhappy path just explode later on. Whether the language should force you to think about unhappy paths is a complex and subjective question that I won't delve into now beyond saying that there's valid arguments and use cases for both designs. As noted in the article above, I've started using enums and union type returns a lot for error handling and it's pretty nice, all things considered. That works today in all supported PHP verisons (8.1+). That said, it's not perfect, in part because it's not standardized and there's no really good language-level automation around it. If we had generics and ADTs, building a Rust-like Result type would be super easy, and I'd suggest we include one in the stdlib for consistency. We'll probably get ADTs eventually, but generics are not something I'd bank on, so that is out. HOWEVER, and this is where the important part lies, an open, lightweight, checked exception system is isomorphic to a Result object. It's just unwrapped. Compare these hypotheticals: class DivByZero {} (this could also be an enum case, but being generic for now.) function divide(float $a, float $b): Result<int, DivByZero> { if ($b === 0) return new Result::Err(new DivByZero()); return new Result::OK($a/$b); } $result = divide(5, 0); $x = match ($result) { is Result::OK($x) => $x, is Result::Err => // Do some kind of error handling. } vs. function divide(float $a, float $b): int raises DivByZero { if ($b === 0) raise new DivByZero(); return new $a/$b; } try { $result = divide(5, 0); // Do stuff with $result, knowing it is valid. } catch (DivByZero) { // Do some kind of error handling. } These two samples *are logically identical*, and even have mostly the same performance characteristics, and both expose useful data to static analyzers. They're just spelled differently. The advantage of the second is that it could be implemented without generics. (ADTs would be an optional nice-to-have.) And if the caller doesn't handle DivByZero, it would try to pass it up to its caller, but being checked it would require the caller to also declare that it can raise DivByZero. The second option could also be improved by other syntactic sugar to make it easier to work with, like Rust has. For example: try $result = divide(5, 0) catch (DivByZero) { // Error handling that evaluates to a value } Or by making null-aware operators (??, ?->, etc.) treat raised light-exceptions as if they were null to make pipelining easier. Or various other ideas I'm just giving examples of for the moment to make the point, but let's not get off on that tangent. (I would also note that I agree entirely such a system should only support objects, not primitives.) This would provide a far better alternative to the "returns value on success or false on failure" anti-pattern throughout the stdlib, and to the common "returns value on success or null on failure or value not found or any other possible issue" pattern common in user-space code. (No, I don't expect us to go back and change all of stdlib; just pointing out that it's a known language limitation, and this would be the solution.) To be clear: I really like this concept and have discussed it with others before, using almost exactly this syntax. I have not proposed it because my read of Internals lately is that there's no stomach for more type-centric behavior, especially with the obvious "But we already have exceptions, what's yer problem?" response (which is valid to a point, but also incomplete for reasons explained above). The responses in this thread so far confirm that fear, but as an optimist I'd be very happy to be proven wrong if there is an appetite for improving error handling via the type system. Absent that, union types and enums (or really any interfaced object) or a purpose-built Either object are the best options today, and while they're not ideal, they're not bad options either. None of that logic or argument requires sh*tting on OOP as a concept or abusing others on the list, however. Doing that only undermines the valid point that there is ample headroom to improve PHP's error handling. --Larry Garfield

Arvids Godjuks

2 years ago
On Tue, 6 Feb 2024 at 19:14, Larry Garfield <larry@garfieldtech.com> wrote:
> On Tue, Feb 6, 2024, at 4:13 PM, Григорий Senior PHP / Разработчик Web > wrote: > > Btw, i agree about Javascript, but on a low level it produces the most > > clean code, because there's no types and rules. All types moved to > > TypeScript's client side compiler. > > > > JS 15 years ago ACCIDENTALLY created a pipeline. Named it "Promise". We > > spent years after to understand that while (true) and then/catch should > be > > different patterns. > > I assume much of this thread is a language-barrier issue, which is making > it more hostile than it needs to be. So let me try and expand a bit, > because I am actually quite sympathetic to the OP's request, though not the > way it's being made. > > First of all, please don't top post. It is considered rude on this list. > GMail makes it a bit annoying to bottom post but it can be done. Please do > so. > > Second, there's considerable prior art and discussion on the topic of > error handling and exceptions. In particular, I recommend this excellent > article by Joe Duffy: > > https://joeduffyblog.com/2016/02/07/the-error-model/ > > And this one from me: > > https://peakd.com/hive-168588/@crell/much-ado-about-null > > Yes, they're long, but error handling is a topic that requires more than > casual thought. > > To summarize the articles for the short of time: > > * Exceptions in many languages (including PHP) are very expensive. The > stack trace is one of the most expensive things PHP does. This is one of > the reasons why exceptions are terrible for flow control. > * Unchecked exceptions (where a function doesn't define what it can throw) > are a great way to break your application in exciting and unpredictable > ways. (This is the other reason exceptions are terrible for flow control.) > * Many people find checked exceptions cumbersome, even though they are > better (for reasons Duffy goes into). This is due mostly to bad design of > checked exceptions in JVM languages. > * The real problem is that we have a channel for a success case (return > value), a channel for catastrophic failure (exceptions), but no channel for > mundane errors (things a responsible developer should expect and know how > to handle gracefully). So those get shoved into one or the other, usually > an exception. > > The need is for a "mundane error" channel. I agree with this. Different > languages handle it in different ways. > > * Go has multi-returns. > * Rust has the Result type (which is an Either monad), and an > auto-propagation operator (?). > * PHP has union types (though that's not a deliberate design, just an > emergent one). > > One of the key differences between different approaches is whether they > force you to handle error cases (Result type) or let you ignore errors and > assume a happy path (Go, PHP), letting an unhappy path just explode later > on. Whether the language should force you to think about unhappy paths is > a complex and subjective question that I won't delve into now beyond saying > that there's valid arguments and use cases for both designs. > > As noted in the article above, I've started using enums and union type > returns a lot for error handling and it's pretty nice, all things > considered. That works today in all supported PHP verisons (8.1+). That > said, it's not perfect, in part because it's not standardized and there's > no really good language-level automation around it. > > If we had generics and ADTs, building a Rust-like Result type would be > super easy, and I'd suggest we include one in the stdlib for consistency. > We'll probably get ADTs eventually, but generics are not something I'd bank > on, so that is out. > > HOWEVER, and this is where the important part lies, an open, lightweight, > checked exception system is isomorphic to a Result object. It's just > unwrapped. Compare these hypotheticals: > > class DivByZero {} > (this could also be an enum case, but being generic for now.) > > function divide(float $a, float $b): Result<int, DivByZero> > { > if ($b === 0) return new Result::Err(new DivByZero()); > return new Result::OK($a/$b); > } > > $result = divide(5, 0); > $x = match ($result) { > is Result::OK($x) => $x, > is Result::Err => // Do some kind of error handling. > } > > vs. > > function divide(float $a, float $b): int raises DivByZero > { > if ($b === 0) raise new DivByZero(); > return new $a/$b; > } > > try { > $result = divide(5, 0); > // Do stuff with $result, knowing it is valid. > } catch (DivByZero) { > // Do some kind of error handling. > } > > These two samples *are logically identical*, and even have mostly the same > performance characteristics, and both expose useful data to static > analyzers. They're just spelled differently. The advantage of the second > is that it could be implemented without generics. (ADTs would be an > optional nice-to-have.) And if the caller doesn't handle DivByZero, it > would try to pass it up to its caller, but being checked it would require > the caller to also declare that it can raise DivByZero. > > The second option could also be improved by other syntactic sugar to make > it easier to work with, like Rust has. For example: > > try $result = divide(5, 0) catch (DivByZero) { // Error handling that > evaluates to a value } > > Or by making null-aware operators (??, ?->, etc.) treat raised > light-exceptions as if they were null to make pipelining easier. Or > various other ideas I'm just giving examples of for the moment to make the > point, but let's not get off on that tangent. (I would also note that I > agree entirely such a system should only support objects, not primitives.) > > This would provide a far better alternative to the "returns value on > success or false on failure" anti-pattern throughout the stdlib, and to the > common "returns value on success or null on failure or value not found or > any other possible issue" pattern common in user-space code. (No, I don't > expect us to go back and change all of stdlib; just pointing out that it's > a known language limitation, and this would be the solution.) > > To be clear: I really like this concept and have discussed it with others > before, using almost exactly this syntax. I have not proposed it because > my read of Internals lately is that there's no stomach for more > type-centric behavior, especially with the obvious "But we already have > exceptions, what's yer problem?" response (which is valid to a point, but > also incomplete for reasons explained above). The responses in this thread > so far confirm that fear, but as an optimist I'd be very happy to be proven > wrong if there is an appetite for improving error handling via the type > system. > > Absent that, union types and enums (or really any interfaced object) or a > purpose-built Either object are the best options today, and while they're > not ideal, they're not bad options either. > > None of that logic or argument requires sh*tting on OOP as a concept or > abusing others on the list, however. Doing that only undermines the valid > point that there is ample headroom to improve PHP's error handling. > > --Larry Garfield > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
Thank you Larry for this interesting summary - didn't remember there was quite a bit a discussion around the topic prior. I lean on the "we have exceptions, just leave it be" side out of practical reasons - the vast majority of OO code has standardized around the approach and interoperability is high. It makes using code that's out there super easy and predictable - almost nobody uses the "return false|0|-1" out there (at least I haven't used code like that except the PHP's stdlib, and even that has been changing little by little). It makes error handling predictable, and considering the type of code we mostly write in PHP - most of the time we leave the catching to the global top-level handler or sentry/bugsnag/etc libraries. Consistency is the word I want to highlight here. For better or for worse - it's the method the PHP ecosystem arrived at and it's the predominant one. Introducing a distinctly different method of error handling is going to bring in wrappers around libraries that convert errors to one style or the other, the application code can end up using different ways of error handling, etc, etc. My approach is to grab a different language aka "the right tool for the job" if I want to build things differently, that's why we have so many programming languages and not just a few :) I'd put resources into optimising the VM and php engine to handle the exceptions better and if there are improvements to be had there - do those maybe? (I suspect JIT is also going to influence this a lot going forward). Sincerely, Arvīds Godjuks +371 26 851 664 arvids.godjuks@gmail.com Telegram: @psihius https://t.me/psihius

Jordan LeDoux

2 years ago
On Tue, Feb 6, 2024 at 11:19 AM Arvids Godjuks <arvids.godjuks@gmail.com> wrote:
> On Tue, 6 Feb 2024 at 19:14, Larry Garfield <larry@garfieldtech.com> > wrote: > > Thank you Larry for this interesting summary - didn't remember there was > quite a bit a discussion around the topic prior. > > I lean on the "we have exceptions, just leave it be" side out of practical > reasons - the vast majority of OO code has standardized around the approach > and interoperability is high. It makes using code that's out there super > easy and predictable - almost nobody uses the "return false|0|-1" out there > (at least I haven't used code like that except the PHP's stdlib, and even > that has been changing little by little). It makes error handling > predictable, and considering the type of code we mostly write in PHP - most > of the time we leave the catching to the global top-level handler or > sentry/bugsnag/etc libraries. > Consistency is the word I want to highlight here. For better or for worse - > it's the method the PHP ecosystem arrived at and it's the predominant one. > Introducing a distinctly different method of error handling is going to > bring in wrappers around libraries that convert errors to one style or the > other, the application code can end up using different ways of error > handling, etc, etc. My approach is to grab a different language aka "the > right tool for the job" if I want to build things differently, that's why > we have so many programming languages and not just a few :) > > I'd put resources into optimising the VM and php engine to handle the > exceptions better and if there are improvements to be had there - do those > maybe? (I suspect JIT is also going to influence this a lot going forward). > > Sincerely, > Arvīds Godjuks >
When what you have is a situation where a function or block of code goes "I know something fixable went wrong, but only the block above me in the execution stack knows what to do about it", Exceptions are extremely overkill. But they are the only "sane" option in PHP in a lot of situations. PHP simply doesn't have a language level structure to handle this VERY COMMON situation. The fact that people have standardized on Exceptions for this is not a point in favor of Exceptions. It is a sign of how much extra performance and semantic correctness we COULD provide to the language by improving this area of error handling. I don't know if the OP of this email thread was referring to this situation. It was honestly very difficult for me to follow what they were even asking for given the language barrier. But I am 1000% behind the problem space that Larry is describing. Exceptions are not a solution to that problem, they are duct tape. Jordan

Григорий Senior PHP / Разработчик Web

2 years ago
Thanks Larry, I will read both articles next weekend. Am not even talking about changing `throw` to `raise`. Am talking only about: - production ready code - that should be able to refactor with error collectors (that was not implemented years ago) - without touching return types - without touching input arguments of existing code - without possible code fall after throw exception: you have to try/catch all places you use that function (sometimes you predict possible error, and yes, write return class/enum to extend/refactor it later) (and yes, if old code did not support returning null/null-object before - you have to refactor return types then) While working with queues you have a list of tasks - then you reduce it to smaller with reducer (unique/filter/merge) - then do some queries - then walk initial data using reduced results: copying reports to save errors/warnings to each task separately It cannot be solved with exceptions. In addition, large arrays throw exceptions that cause timeloss. It's definitely not a tool for. Also your method could return many errors (today - only one error/exception), and you need to write a second method, then call the second method, then debug the second method. So what's in rest? Arrays collection of warnings and errors. Changing return types or passing second-return by reference. [ Enum case ~ DTO output ] covers newly written code. Old code is uncovered. You have to rewrite a full tree, that's why some trick is necessary. I did it my way with an error bag stack. I enable it inside the function or in place I call the function. I want to share this experience, and imagined it would be better for all users. It could be done without 2 classes, 10 functions and work with push/pop/current (closer to ob_start/ob_get_clean story). I guess it could be implemented if `raise` world will put any data to the current error bag in the stack. Exactly if the current error bag is present (declared manually like you can declare() strict types or ticks for some scope). I agree that there's more mandatory problems to solve that I didn't even know about. I tried to talk about error handling with a few developers, all of them recommend: 1. Use exceptions, don't make anything fresh 2. Do validation at the script start to reduce the count of errors later I've just encountered cases where bugs come from within - once you integrate a really bad external system with its own checks, which are described in hundreds of documents, I'm sure you'll encounter new bugs once the "working" code is released to production. And then you will need to quickly and easily reorganize it. And you can't. And you will be sad. And, "PHP moves differently" is a completely wrong principle, I believe in "watching for".
-- +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram 6562680@gmail.com

Larry Garfield

2 years ago
On Tue, Feb 6, 2024, at 7:56 PM, Григорий Senior PHP / Разработчик Web wrote:
> Thanks Larry, I will read both articles next weekend. > > Am not even talking about changing `throw` to `raise`. > > Am talking only about: > - production ready code > - that should be able to refactor with error collectors (that was not > implemented years ago) > - without touching return types > - without touching input arguments of existing code > - without possible code fall after throw exception: you have to try/catch > all places you use that function (sometimes you predict possible error, and > yes, write return class/enum to extend/refactor it later) > (and yes, if old code did not support returning null/null-object before - > you have to refactor return types then) > > While working with queues you have a list of tasks > - then you reduce it to smaller with reducer (unique/filter/merge) > - then do some queries > - then walk initial data using reduced results: copying reports to save > errors/warnings to each task separately > > It cannot be solved with exceptions. In addition, large arrays throw > exceptions that cause timeloss. It's definitely not a tool for. > Also your method could return many errors (today - only one > error/exception), and you need to write a second method, then call the > second method, then debug the second method. > > So what's in rest? Arrays collection of warnings and errors. Changing > return types or passing second-return by reference. > > [ Enum case ~ DTO output ] covers newly written code. Old code is > uncovered. You have to rewrite a full tree, that's why some trick is > necessary. > > I did it my way with an error bag stack. I enable it inside the function or > in place I call the function. I want to share this experience, and imagined > it would be better for all users. It could be done without 2 classes, 10 > functions and work with push/pop/current (closer to ob_start/ob_get_clean > story). > I guess it could be implemented if `raise` world will put any data to the > current error bag in the stack. Exactly if the current error bag is present > (declared manually like you can declare() strict types or ticks for some > scope). > > I agree that there's more mandatory problems to solve that I didn't even > know about. > I tried to talk about error handling with a few developers, all of them > recommend: > 1. Use exceptions, don't make anything fresh > 2. Do validation at the script start to reduce the count of errors later > > I've just encountered cases where bugs come from within - once you > integrate a really bad external system with its own checks, which are > described in hundreds of documents, I'm sure you'll encounter new bugs once > the "working" code is released to production. And then you will need to > quickly and easily reorganize it. > > And you can't. > And you will be sad. > And, "PHP moves differently" is a completely wrong principle, I believe in > "watching for".
I think there's a subtle but important difference here between what you're describing as the problem and what you implied the solution was (which I then ran with). What you're talking about is trying to change the error handling model of existing code without changing function signatures. There are only two possible ways to do that, both of them bad: Unchecked exceptions and globals. What I described, based on the syntax you offered, is checked exceptions, which necessarily means changing the function signature. Error handling is part of the contract of a function. If its error handling changes, it *should* have a signature change to indicate that. (That unchecked exceptions do not do that is the problem with unchecked exceptions.) So if "no changes to existing code" is the goal, checked exceptions as I describe them are not the answer you are looking for. It seems from your latest message that you're describing more a generalized version of `json_last_error()` and similar functions. The problem there is that such an API design is generally considered very poor practice outside of C, because it's all necessarily based on globals and "hope you remembered to check the thing that no one told you to check and is not even slightly obvious to check". That is not something I would want better support for in the language at all. There's probably cleaner ways to emulate it in user-space, but that is for a particular application to sort out. There's definitely cleaner monadic solutions (which I've written before and are quite neat) using a writer/logger monad, but that again doesn't meet your "don't change existing code" requirement. I don't think anything the language can do will meet that requirement and be a good design. --Larry Garfield

Larry Garfield

2 years ago
On Tue, Feb 6, 2024, at 7:18 PM, Arvids Godjuks wrote:
>> To be clear: I really like this concept and have discussed it with others >> before, using almost exactly this syntax. I have not proposed it because >> my read of Internals lately is that there's no stomach for more >> type-centric behavior, especially with the obvious "But we already have >> exceptions, what's yer problem?" response (which is valid to a point, but >> also incomplete for reasons explained above). The responses in this thread >> so far confirm that fear, but as an optimist I'd be very happy to be proven >> wrong if there is an appetite for improving error handling via the type >> system. >> >> Absent that, union types and enums (or really any interfaced object) or a >> purpose-built Either object are the best options today, and while they're >> not ideal, they're not bad options either. >> >> None of that logic or argument requires sh*tting on OOP as a concept or >> abusing others on the list, however. Doing that only undermines the valid >> point that there is ample headroom to improve PHP's error handling. >> >> --Larry Garfield >> >> -- >> PHP Internals - PHP Runtime Development Mailing List >> To unsubscribe, visit: https://www.php.net/unsub.php >> >> > Thank you Larry for this interesting summary - didn't remember there was > quite a bit a discussion around the topic prior. > > I lean on the "we have exceptions, just leave it be" side out of practical > reasons - the vast majority of OO code has standardized around the approach > and interoperability is high. It makes using code that's out there super > easy and predictable - almost nobody uses the "return false|0|-1" out there > (at least I haven't used code like that except the PHP's stdlib, and even > that has been changing little by little). It makes error handling > predictable, and considering the type of code we mostly write in PHP - most > of the time we leave the catching to the global top-level handler or > sentry/bugsnag/etc libraries. > Consistency is the word I want to highlight here. For better or for worse - > it's the method the PHP ecosystem arrived at and it's the predominant one. > Introducing a distinctly different method of error handling is going to > bring in wrappers around libraries that convert errors to one style or the > other, the application code can end up using different ways of error > handling, etc, etc. My approach is to grab a different language aka "the > right tool for the job" if I want to build things differently, that's why > we have so many programming languages and not just a few :) > > I'd put resources into optimising the VM and php engine to handle the > exceptions better and if there are improvements to be had there - do those > maybe? (I suspect JIT is also going to influence this a lot going forward).
"The right tool for the job" is indeed the strongest argument for lightweight exceptions. It's a tool we lack right now. I'm thinking not of "DB went away" type issues (Exceptions are already fine there), but "requested product not found." Right now, the options we have are: public function find($id): ?Product {} public function find($id): Product { // This is very expensive I don't think will ever not be. // It will also bubble up to the top of the application and crash the whole process, // Or still show up in weird, unexpected places. throw new NotFound(); } public function find($id): Product|RepoError {} enum RepoError { case NotFound; } The first is probably most common, but null (as I go into in the article) doesn't tell you anything and leads to mismatch errors. Exceptions, I'd argue, are just plain wrong in this situation. (Which means, yes, all the frameworks that throw exceptions on route-not-found are doing it wrong.) And the union-enum approach is a bit clunky as it has no native language support, and no solid conventions behind it. This is my preferred approach personally today, but I think we can do better. Even just having this available at all means that "well everyone just uses unchecked exceptions" isn't entirely true. (All three of the above can be found in the wild.) --Larry Garfield

Arvids Godjuks

2 years ago
On Tue, 6 Feb 2024 at 22:09, Larry Garfield <larry@garfieldtech.com> wrote:
> On Tue, Feb 6, 2024, at 7:18 PM, Arvids Godjuks wrote: > > >> To be clear: I really like this concept and have discussed it with > others > >> before, using almost exactly this syntax. I have not proposed it > because > >> my read of Internals lately is that there's no stomach for more > >> type-centric behavior, especially with the obvious "But we already have > >> exceptions, what's yer problem?" response (which is valid to a point, > but > >> also incomplete for reasons explained above). The responses in this > thread > >> so far confirm that fear, but as an optimist I'd be very happy to be > proven > >> wrong if there is an appetite for improving error handling via the type > >> system. > >> > >> Absent that, union types and enums (or really any interfaced object) or > a > >> purpose-built Either object are the best options today, and while > they're > >> not ideal, they're not bad options either. > >> > >> None of that logic or argument requires sh*tting on OOP as a concept or > >> abusing others on the list, however. Doing that only undermines the > valid > >> point that there is ample headroom to improve PHP's error handling. > >> > >> --Larry Garfield > >> > >> -- > >> PHP Internals - PHP Runtime Development Mailing List > >> To unsubscribe, visit: https://www.php.net/unsub.php > >> > >> > > Thank you Larry for this interesting summary - didn't remember there was > > quite a bit a discussion around the topic prior. > > > > I lean on the "we have exceptions, just leave it be" side out of > practical > > reasons - the vast majority of OO code has standardized around the > approach > > and interoperability is high. It makes using code that's out there super > > easy and predictable - almost nobody uses the "return false|0|-1" out > there > > (at least I haven't used code like that except the PHP's stdlib, and even > > that has been changing little by little). It makes error handling > > predictable, and considering the type of code we mostly write in PHP - > most > > of the time we leave the catching to the global top-level handler or > > sentry/bugsnag/etc libraries. > > Consistency is the word I want to highlight here. For better or for > worse - > > it's the method the PHP ecosystem arrived at and it's the predominant > one. > > Introducing a distinctly different method of error handling is going to > > bring in wrappers around libraries that convert errors to one style or > the > > other, the application code can end up using different ways of error > > handling, etc, etc. My approach is to grab a different language aka "the > > right tool for the job" if I want to build things differently, that's why > > we have so many programming languages and not just a few :) > > > > I'd put resources into optimising the VM and php engine to handle the > > exceptions better and if there are improvements to be had there - do > those > > maybe? (I suspect JIT is also going to influence this a lot going > forward). > > > "The right tool for the job" is indeed the strongest argument for > lightweight exceptions. It's a tool we lack right now. > > I'm thinking not of "DB went away" type issues (Exceptions are already > fine there), but "requested product not found." Right now, the options we > have are: > > public function find($id): ?Product {} > > public function find($id): Product { > // This is very expensive I don't think will ever not be. > // It will also bubble up to the top of the application and crash the > whole process, > // Or still show up in weird, unexpected places. > throw new NotFound(); > } > > public function find($id): Product|RepoError {} > enum RepoError { > case NotFound; > } > > The first is probably most common, but null (as I go into in the article) > doesn't tell you anything and leads to mismatch errors. > > Exceptions, I'd argue, are just plain wrong in this situation. (Which > means, yes, all the frameworks that throw exceptions on route-not-found are > doing it wrong.) > > And the union-enum approach is a bit clunky as it has no native language > support, and no solid conventions behind it. This is my preferred approach > personally today, but I think we can do better. Even just having this > available at all means that "well everyone just uses unchecked exceptions" > isn't entirely true. (All three of the above can be found in the wild.) > > --Larry Garfield >
And that, folks, is how you change people's minds. I'm on board, Larry. I agree that things like route not found, entity not found and so on don't have to be full-fat exceptions - in those cases, you indeed don't need the stack trace and other parts of it. Even the error message might not be required since whatever you "throw" or "raise" in this case is self-explanatory just by its type/object/enum type.
-- Arvīds Godjuks +371 26 851 664 arvids.godjuks@gmail.com Telegram: @psihius https://t.me/psihius

Weedpacket

2 years ago
On 2024-02-07 09:08, Larry Garfield wrote:
> > "The right tool for the job" is indeed the strongest argument for lightweight exceptions. It's a tool we lack right now. > > I'm thinking not of "DB went away" type issues (Exceptions are already fine there), but "requested product not found." Right now, the options we have are: > > ... > > The first is probably most common, but null (as I go into in the article) doesn't tell you anything and leads to mismatch errors. > > Exceptions, I'd argue, are just plain wrong in this situation. (Which means, yes, all the frameworks that throw exceptions on route-not-found are doing it wrong.) > > And the union-enum approach is a bit clunky as it has no native language support, and no solid conventions behind it. This is my preferred approach personally today, but I think we can do better. Even just having this available at all means that "well everyone just uses unchecked exceptions" isn't entirely true. (All three of the above can be found in the wild.) >
I can add a fourth option for the "get record by ID" that may not be found, that I've seen in the wild: public function find($id): ProductSet {} Where the ProductSet is a collection of (in this case) no more than one Product. It's up to the caller to verify there actually is a Product inside and extract it, and it's also the caller's decision whether not finding one is a problem or not. Of course, in this domain it folded in with using ProductSets to represent more general collections of Products and the resulting set algebra. Weedpacket

Alex Wells

2 years ago
On Tue, Feb 6, 2024 at 7:14 PM Larry Garfield <larry@garfieldtech.com> wrote:
> These two samples *are logically identical*, and even have mostly the same > performance characteristics, and both expose useful data to static > analyzers. They're just spelled differently. The advantage of the second > is that it could be implemented without generics. (ADTs would be an > optional nice-to-have.) And if the caller doesn't handle DivByZero, it > would try to pass it up to its caller, but being checked it would require > the caller to also declare that it can raise DivByZero. >
Let's assume that the developer knows the divisor isn't 0 - through an assertion or an `if` clause above the call to `divide(5, $divisor)`. In this case, DivByZero error cannot ever be thrown (or risen), but the developer would still have to either handle the error (which will never happen) or declare it as raisable, which in turn may require also marking 10+ function/method calls as "raises DivByZero". Both options aren't great. And even if there was no assertion about the divisor, maybe the developer's intent is exactly to ignore that case as an "implicit assertion" - meaning instead of explicitly asserting the divisor value themselves (through `assert($divisor !== 0)`), they rely on `divide(5, $divisor)` doing that implicitly for them. If the `assert()` fails, then nobody is expected to really handle that assertion error; it usually bubbles up to the global exception handler which takes care of it. If the `divide()` fails on the other hand, checked exceptions would require all the callers to actually "check" it by catching or declaring the caller function as `raises DivByZero`, but this doesn't bring any benefit to the developer in this case. So I assume this is why Java developers hate checked exceptions and why Kotlin doesn't have them. I'm not aware of other implementations of checked exceptions; there may be other, better versions of them. If you have any in mind that overcome the issues above, I'd be interested to look into them :)

Григорий Senior PHP / Разработчик Web

2 years ago
Thanks for suggestion about assert() use cases, i know it exists before, but never used. Usually if something shouldn't happen i throw exception there, or at least type control. Its like manual mark for the future - you doing something wrong, fix it. Error collection case its about "you're right, but external system doesnt think so". Example 1: i send correctly and valid first name (for my system) to external system. That system reported thats name contains character outside its regular expression. I can solve it in-place creating function with regular expression that change invalid chars to question marks and case will be covered. Example 2: My system result is less than zero. Remote system expects greater than zero. I cant just validate/sanitize data to greater than zero. If i send single operation - it could be the exception. But there could be two cases - multiple operations (commands) queue or bulk operation (one command, few rows of different data). In first case i have to stop all operation, exception could help me. In second case same error becomes to warning, and one row should not stop whole process, but have to report me. So previously i solved it with exception, and now exception shots my legs. Thats why i use exceptions only if something wrong on developer/user side, and never to system-system cases. if() still powerful. ср, 7 февр. 2024 г. в 03:55, Alex Wells <autaut03@gmail.com>:
> On Tue, Feb 6, 2024 at 7:14 PM Larry Garfield <larry@garfieldtech.com> > wrote: > > > These two samples *are logically identical*, and even have mostly the > same > > performance characteristics, and both expose useful data to static > > analyzers. They're just spelled differently. The advantage of the > second > > is that it could be implemented without generics. (ADTs would be an > > optional nice-to-have.) And if the caller doesn't handle DivByZero, it > > would try to pass it up to its caller, but being checked it would require > > the caller to also declare that it can raise DivByZero. > > > > Let's assume that the developer knows the divisor isn't 0 - through an > assertion or an `if` clause above the call to `divide(5, $divisor)`. In > this case, DivByZero error cannot ever be thrown (or risen), but the > developer would still have to either handle the error (which will never > happen) or declare it as raisable, which in turn may require also marking > 10+ function/method calls as "raises DivByZero". Both options aren't great. > > And even if there was no assertion about the divisor, maybe the developer's > intent is exactly to ignore that case as an "implicit assertion" - meaning > instead of explicitly asserting the divisor value themselves (through > `assert($divisor !== 0)`), they rely on `divide(5, $divisor)` doing that > implicitly for them. If the `assert()` fails, then nobody is expected to > really handle that assertion error; it usually bubbles up to the global > exception handler which takes care of it. If the `divide()` fails on the > other hand, checked exceptions would require all the callers to actually > "check" it by catching or declaring the caller function as `raises > DivByZero`, but this doesn't bring any benefit to the developer in this > case. > > So I assume this is why Java developers hate checked exceptions and why > Kotlin doesn't have them. I'm not aware of other implementations of checked > exceptions; there may be other, better versions of them. If you have any in > mind that overcome the issues above, I'd be interested to look into them :) >
-- +375 (29) 676-48-68 <+375296764868> / Mobile - предпочитаемый способ связи https://t.me/gzhegow / https://t.me/%2B375296764868 / Telegram 6562680@gmail.com

Larry Garfield

2 years ago
On Wed, Feb 7, 2024, at 12:55 AM, Alex Wells wrote:
> On Tue, Feb 6, 2024 at 7:14 PM Larry Garfield <larry@garfieldtech.com> > wrote: > >> These two samples *are logically identical*, and even have mostly the same >> performance characteristics, and both expose useful data to static >> analyzers. They're just spelled differently. The advantage of the second >> is that it could be implemented without generics. (ADTs would be an >> optional nice-to-have.) And if the caller doesn't handle DivByZero, it >> would try to pass it up to its caller, but being checked it would require >> the caller to also declare that it can raise DivByZero. >> > > Let's assume that the developer knows the divisor isn't 0 - through an > assertion or an `if` clause above the call to `divide(5, $divisor)`. In > this case, DivByZero error cannot ever be thrown (or risen), but the > developer would still have to either handle the error (which will never > happen) or declare it as raisable, which in turn may require also marking > 10+ function/method calls as "raises DivByZero". Both options aren't great. > > And even if there was no assertion about the divisor, maybe the developer's > intent is exactly to ignore that case as an "implicit assertion" - meaning > instead of explicitly asserting the divisor value themselves (through > `assert($divisor !== 0)`), they rely on `divide(5, $divisor)` doing that > implicitly for them. If the `assert()` fails, then nobody is expected to > really handle that assertion error; it usually bubbles up to the global > exception handler which takes care of it. If the `divide()` fails on the > other hand, checked exceptions would require all the callers to actually > "check" it by catching or declaring the caller function as `raises > DivByZero`, but this doesn't bring any benefit to the developer in this > case. > > So I assume this is why Java developers hate checked exceptions and why > Kotlin doesn't have them. I'm not aware of other implementations of checked > exceptions; there may be other, better versions of them. If you have any in > mind that overcome the issues above, I'd be interested to look into them :)
Re assertions: The problem with assertions is they can be disabled. They're really *only* useful as an extra "extended type check", and then only in dev. That makes them unreliable, so using them for flow control is right out. And in practice they just turn into exceptions anyway (or Throwables at least), so there's really no benefit over just using a Throwable if you're going to insist they aren't disabled for the code to work. The Joe Duffy article I linked above describes the issues with Java's exception design. Mainly, it's only mostly-checked. It forces you to declare your throwables... but certain types of throwables don't need to be declared, which means as a consumer of a function, you have no guarantee that a function that has no declared throws will actually never throw. So you get all the pain, none of the gain. (This is admittedly a challenge for introducing them to PHP as well, which is why I am proposing a separate syntax from exceptions since they would serve a different purpose.) As discussed in the article, Midori (the experimental language for which Duffy was tech lead) had checked exceptions that worked essentially as I have proposed here. What made them work is * They were very lightweight. * They were firmly and strictly checked, without any "holes" in the design like Java. * They were used locally, as an unwrapped Either monad, rather than for up-the-stack communication. * Midori had a much more robust type checker than Java, so more errors could be moved to the type system and eliminated entirely. * The built-in type hierarchy was more sensible than Java's. * Midori has guards, which eliminate 99% of cases. It's essentially promoting assertion-esque type checking into the function signature. That is, DivByZero wouldn't even be an exception, it would be a runtime enforced type error. I'd love to have these, too, but that's not the topic right now. :-) Guards would look something like this (using Midori-inspired syntax): function divide(float $a, float $b): float require $b !== 0 ensures return != INF { // ... } (In Midori, those could either be materialized into code or compiled away if the compiler could guarantee they held true. In PHP I don't think we could compile them away so they'd have to be materialized, but it would make them more apparent to static analyzers as well as better communicate intent to other developers.) The article goes into much more detail, and I really do encourage reading it. To your specific question about prior knowledge (eg, non-zero), there's a couple of ways, conceptually, to address that. 1. A more robust type system can handle things like non-zero-int or unsigned-int as a type. (I believe Midori has this, but honestly it's unlikely for PHP.) 2. Guard clauses. 3. Better syntax making handling "no op errors" easier. For the third, just to spitball: function divide(float $a, float $b): int raises DivByZero { if ($b === 0) raise new DivByZero(); return new $a/$b; } $result = try divide(5, 0) on DivByZero null; // Equivalent to: try { $result = divide(5, 0); } catch (DivByZero) { $result = null; } But the basic point is that DivByZero is probably a bad use case example as that should be an Abandonment case (ie, type failure), just the easiest one I came up with on the spot. :-) To use the more practical example, findProduct(int $id): Product raises ProductNotFound { } function mycontroller(int $id) { $product = try findProduct($id) on ProductNotFound return view('not_found'); return view('product', $product); } If you're 100% certain the ID is valid, you could do "on ProductNotFound null" as a no-op case. However, I suspect in practice that is a minority case. Routing would probably be more like this: function findRoute(Request $request): Route raises RoutingError try $route = findRoute($request); catch (RouteNotFound $r) { // Do stuff. } catch (MethodNotAllowed $r) { // Do stuff. } // ... (Though in fairness, I'd probably use a proper monad for routing instead anyway.) --Larry Garfield

Григорий Senior PHP / Разработчик Web

2 years ago
I still don't understand why the problem is signature and moving a simple throw statement to return type, and then rewriting the catch statement to oneline-r. I am completely satisfied with the php way of working with method signatures except non-critical cases: - still no `undefined` type, so some functions have limited functionality with func_num_args() inside - no languages still implement emptiness check on argument types. empty string should always be additionally asserted, like positive-negative numbers, and nan/inf with float stuff (nan/inf is too rarely case) - I'd say generics support but it is fully covered with phpdoc. use argument Generic, mark it as class-string and @template and it works well Try/catch statements don't scare me. I feel the trouble in missing try/catch statements and requirements to check all method call places once you throw a non-critical error just because another way, sorry, necessary way, is too hard for fast implementation. That trouble arrives only when you move from single-task script to multi-task script. The nearest example is SQL, which always works with rows and never returns one value, response is always "list of rows". So the primary method of error handling should be focused on batch processing, but `throw` keyword is very handy for filters, assertions, validators and low level static functions. If you finish your code fully covered with exceptions - once you try to run several times the same with try/catch over method calls - the original action code could have been better by catching some exceptions on its own. However, it is closed to you and now if you want to break it into two parts, you will have to rewrite it completely, because it is completely closed to you and the slightest exception that was not caught in itself (and not in you) will break it entirely, and will not disable part of it. The exception was invented in order to shift the closure of the problem to the next level of developers. However, it is the exception that prevents these developers from changing the source logic of the code that throws it and continuing to perform the action intended in the source code - the exception simply breaks the entire branch (following `encapsulation`). This means it should break only the most primitive functions that can be replaced quickly. This is where the so-called SOLID came from, which forces everything to be broken down into molecules instead of first closing the problem and then deciding whether to break it into parts or leave it that way.