Hello Guile Users! I have a question regarding exception creation in general and maybe in specific about irritants. I created a new exception type: ~~~~ (library (exceptions) (export make-contract-violated-exception-plain make-exception-contract-violated-compound &contract-violated contract-violated-exception?) (import (except (rnrs base) let-values) (only (guile) lambda* λ record-constructor make-exception-type &programming-error) (ice-9 exceptions)) ;; Create a custom exception type, to make it clearer, ;; that a contract failed, and not only an arbitrary ;; assertion. (define &contract-violated (make-exception-type ;; name of the new exception type '&contract-violated ;; parent exception type &programming-error ;; list of values the constructor of the exception ;; takes and their names in the record '())) (define make-contract-violated-exception-plain ;; record-constructor is a procedure, which will return ;; the constructor for any record. (record-constructor ;; Create an exception type, which is a record. This ;; record has a constructor, which we can name using ;; define for example. &contract-violated)) (define contract-violated-exception? (exception-predicate &contract-violated)) (define make-exception-contract-violated-compound (λ (message origin irritants) (make-exception (make-contract-violated-exception-plain) (make-exception-with-message message) (make-exception-with-origin origin) (make-exception-with-irritants irritants))))) ~~~~ However, I realized, when a contract is violated, it would be nice to not only see the violated contract or condition as irritants, but also the values of the irritants. So I simply tried adding them as well in the exception: ~~~~ ... (define make-exception-contract-violated-compound (λ (message origin irritants irritant-values) (make-exception (make-contract-violated-exception-plain) (make-exception-with-message message) (make-exception-with-origin origin) (make-exception-with-irritants irritants) (make-exception-with-irritants irritant-values)))) ... ~~~~ Note, that now I have 2 times `make-exception-with-irritants` in there. This does not cause an error and `exception-irritants` still returns the first irritants, so my tests also all still pass, as I have not tested for there not to be other exception attributes. For example a violated exception example could look like this: ~~~~ (define-with-contract bla (require (> foo 10)) (ensure (> <?> 0)) (λ (foo) (- 20 foo))) (bla 10) ice-9/boot-9.scm:1685:16: In procedure raise-exception: ERROR: 1. &contract-violated 2. &message: "contract violated" 3. &origin: bla 4. &irritants: (> foo 10) 5. &irritants: (> 10 10) ~~~~ Nice! Now I have the value of `foo` in this case as well and that could be useful information in cases, when I get a violated contract unexpectedly. However, having irritants twice seems a bit weird. Is this something, that is safe to do? Something expected and probably unchanging in future versions of GNU Guile? Or does it merely work by chance? I could always make another exception type like "exception-with-irritant-values" or something and use that, instead of a second "with irritants" call. Best regards, Zelphir -- repositories:https://notabug.org/ZelphirKaltstahl
On 11/25/22 01:03, Zelphir Kaltstahl wrote: > Hello Guile Users! > > I have a question regarding exception creation in general and maybe in > specific about irritants. > > I created a new exception type: > > ~~~~ > (library (exceptions) > (export make-contract-violated-exception-plain > make-exception-contract-violated-compound > &contract-violated > contract-violated-exception?) > (import (except (rnrs base) let-values) > (only (guile) > lambda* λ > record-constructor > make-exception-type > &programming-error) > (ice-9 exceptions)) > > ;; Create a custom exception type, to make it clearer, > ;; that a contract failed, and not only an arbitrary > ;; assertion. > (define &contract-violated > (make-exception-type > ;; name of the new exception type > '&contract-violated > ;; parent exception type > &programming-error > ;; list of values the constructor of the exception > ;; takes and their names in the record > '())) > > (define make-contract-violated-exception-plain > ;; record-constructor is a procedure, which will return > ;; the constructor for any record. > (record-constructor > ;; Create an exception type, which is a record. This > ;; record has a constructor, which we can name using > ;; define for example. > &contract-violated)) > > (define contract-violated-exception? > (exception-predicate &contract-violated)) > > (define make-exception-contract-violated-compound > (λ (message origin irritants) > (make-exception > (make-contract-violated-exception-plain) > (make-exception-with-message message) > (make-exception-with-origin origin) > (make-exception-with-irritants irritants))))) > ~~~~ > > However, I realized, when a contract is violated, it would be nice to not only > see the violated contract or condition as irritants, but also the values of > the irritants. So I simply tried adding them as well in the exception: > > ~~~~ > ... > (define make-exception-contract-violated-compound > (λ (message origin irritants irritant-values) > (make-exception > (make-contract-violated-exception-plain) > (make-exception-with-message message) > (make-exception-with-origin origin) > (make-exception-with-irritants irritants) > (make-exception-with-irritants irritant-values)))) > ... > ~~~~ > > Note, that now I have 2 times `make-exception-with-irritants` in there. This > does not cause an error and `exception-irritants` still returns the first > irritants, so my tests also all still pass, as I have not tested for there not > to be other exception attributes. > > For example a violated exception example could look like this: > > ~~~~ > (define-with-contract bla > (require (> foo 10)) > (ensure (> <?> 0)) > (λ (foo) > (- 20 foo))) > > (bla 10) > > ice-9/boot-9.scm:1685:16: In procedure raise-exception: > ERROR: > 1. &contract-violated > 2. &message: "contract violated" > 3. &origin: bla > 4. &irritants: (> foo 10) > 5. &irritants: (> 10 10) > ~~~~ > > Nice! Now I have the value of `foo` in this case as well and that could be > useful information in cases, when I get a violated contract unexpectedly. > > However, having irritants twice seems a bit weird. Is this something, that is > safe to do? Something expected and probably unchanging in future versions of > GNU Guile? Or does it merely work by chance? > > I could always make another exception type like > "exception-with-irritant-values" or something and use that, instead of a > second "with irritants" call. > > Best regards, > Zelphir Hi! Does anyone know this? Is it OK and reliable to have more than 1 `exception-with-irritant-values` in a compound exception? Regards, Zelphir -- repositories: https://notabug.org/ZelphirKaltstahl
[-- Attachment #1.1.1: Type: text/plain, Size: 2257 bytes --] On 25-11-2022 01:03, Zelphir Kaltstahl wrote: > ice-9/boot-9.scm:1685:16: In procedure raise-exception: > ERROR: > 1. &contract-violated > 2. &message: "contract violated" > 3. &origin: bla > 4. &irritants: (> foo 10) > 5. &irritants: (> 10 10) I don't understand this -- (> 10 10) is always false, no? I would interpret such an error message as 'you can't call this procedure at all, it requires a contradiction to hold'. What's the purpose of (> 10 10) here? I guess 'foo=10'? If you want to include the value of 'foo', this can be made more explicit, e.g.: 4. &unsatisfied-constraint: (> foo 10) 5. &argument: foo, 10 (i.e. this &argument condition has two fields) That would avoid the vague '&irritants' -- I mean, A base type used for storing information about the causes of another condition in a compound condition. is rather vague, it could refer to 'the condition causing this condition', or the values of the arguments, or some property of those arguments, or ... > ~~~~ > > Nice! Now I have the value of `foo` in this case as well and that could > be > useful information in cases, when I get a violated contract unexpectedly. > > However, having irritants twice seems a bit weird. Is this something, > that is safe to do? Something expected and probably unchanging in future > versions of GNU Guile? Or does it merely work by chance? > > I could always make another exception type like > "exception-with-irritant-values" > or something and use that, instead of a second "with irritants" call. I don't know if having multiple instances of the same condition type inside a single condition is supported. However, given that &irritants is plural (instead of a singular &irritant), and the documentation also mentions 'causes' (plural!): A base type used for storing information about the causes of another condition in a compound condition. ... why not make the value of &irritants a list, e.g.: > 1. &contract-violated > 2. &message: "contract violated" > 3. &origin: bla > 4. &irritants: ((> foo 10) (> 10 10)) Greetings, Maxime. [-- Attachment #1.1.2: OpenPGP public key --] [-- Type: application/pgp-keys, Size: 929 bytes --] [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 236 bytes --]