Ways to handlle errors lazarus / 21-General ---------------------------------------------------------------------- jollytall · 2022-02-14 20:20 Sorry for the long post. It is only partly a question, partly a summary of facts, partly a trigger for discussion. The question is how to handle an error in a method (procedure or function)? Does it make sense to make a consistent approach, or use always one, that fits the actual method best? Over my 40 years of programming I saw many approaches, all have benefits and problems. Below I summarize many, but probably you can add even some more. Also it is possible to combine some of them within the same method. Imagine a mathematical function: f(a,b,c) = sqrt(a/(b-c)). If b=c or a/(b-c) is negative, the function shall fail somehow. I can think of the following approaches (some are a bit "strange" for this particular function, but I hope the intention is clear): 1. Return the best value and set a global error code variable to a certain code. This approach is used in many libraries, think e.g. IOResult. Pro: The program is not interrupted, the caller can decide to check the error code or not (if it is sure, it is OK) and what to do with a certain error. The result (return value) of the function can be used immediately, e.g. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i d := f(a,b,c);Con: If the error code is not read and handled properly the error can go on, causing bigger and more difficult-to-debug problems later. Every library (unit) need its own global error variable and the users has to know and remember them. 2. Return the result if no error, or return a special value (or a set of special values) if the operation failed. This is also very common, when e.g. the handle of a resource is expected as a result, and -1 is returned when the resource cannot be returned (e.g. the number of bytes read from a socket). If the correct result is always positive (like in the above example) then even multiple negative numbers can be used, e.g. -1 means a=b, -2 means a/(b-c) is negative. Pro: The result and the error code can be retrieved in one step. In some cases even multiple values can be returned. Con: If the result can be any value (positive, negative, zero) then it is more difficult to use it for error reporting as well (although NaN can be used) If we want to use the result, more lines of code are needed: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  d : double;...d := f(a,b,c);if d < 0 then  errorelse  process;... 3. Move the result in a var (out) parameter and use the return value of the function for error reporting only (either as a boolean or as a multi value error code). Pro: Works well with methods with multiple return values, e.g. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function LinReg(Xs : array of double; Ys : array of double; var a : double; var b: double) : boolean;The result can be checked in the same step when it is called, a useful feature when multiple functions are or-ed: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if Linest(Xarr, Yarr, a, b) or Linest(Xarr, Zarr, a, b) then  writeln('Solved ',a,' ',b); Con: The real result is always a parameter, and hence require a variable and more code-lines to retrieve and use it. 4. Move the error code as a return parameter and the result as the return value of the function, e.g. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function f(a, b, c : double; var resultOK : boolean) : double;Pro: The result can be used easily and error code can be checked if wanted, but can be omitted if the successful execution is guaranteed by the caller. Con: For every call an error variable must be passed (a var parameter cannot be optional) or two overloaded versions of the same function are needed to return or not to return a code. 5. Do whatever the method can and raise an exception if an error found Pro: Very clear code if the exceptions are not handled immediately at every call of the method (but it only makes sense if exceptions are very rare). The caller can decide what to do with the exception. Procedures can also be used if no return value (or multiple return values as in the split example and we do not want to  treat one differently than the other). Con: For proper handling need a lot of try..except block, making the code very complicated and I think also slower. The return value (e.g. best guess) cannot be retrieved if an exception is raised. 6. A bit more sophisticated approach of 5. is to add one more optional parameter, whether to raise an exception or not. Think of a StringSplit function what shall split the function at the first occurrence of a given characters. In some calls it is a fatal error if the given character is not found in the input, while in other cases it is totally normal. E.g. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure StringSplit(Original : string; Separator : Char; var Before : string; var after : string; RaiseExceptions : boolean = true);Pro: A bit even better than 5, as it is easier to control the exception raising, still if the default value of the optional parameter is set to the more often used value, it can be very clear code. Con: Still looks a bit overkill. 7. "Delegate" the error handling to the method called. If a multi- functional error handling/log system is included in the program, then the called method can categorize the errors for different severity levels and call the respective error routine before returning. The caller module can set, what to do at certain error levels and hence still can control a bit what shall happen with an error (e.g. just to log, or to raise exception or to exit the program). Pro: The cleanest code as the caller has nothing to do around the actual call, but can still set some rules in advance. Con: Requires a special module for error handling. 8. Almost any combination of the above can be implemented at the same time, adding up both the Pro-s and the Con-s. ---------------------------------------------------------------------- Zvoni · 2022-02-15 07:37 in 20 years of VB6/VBA and 4 years of FPC/LAZ , in 95% of cases i go with your variant "3": Return the Result as a var-Param, and the Function-result as error-reporting. Depending on the "complexity" of the function it might be a simple boolean, if more "error"-states are possible i use an integer or Enum. What you wrote about using the function -result in an If-Statement (Or-ed/and-ed, whatever): Usually during coding i put the result in a dedicated variable before entering the If-Statement. IMO it's way easier to debug. Considering that 90% of the WinAPI works this way i can stay in the workflow. Only for Functions i know cannot cause an error i use the function result to return the result ---------------------------------------------------------------------- 440bx · 2022-02-15 07:51 @jollytall, I just wanted to second Zvoni 's comments. In the great majority of cases, the better approach is the third one, i.e, return a result indicating success or failure and the output value in a separate parameter. I also suggest that immediately upon entry, the function set the returned value to some known/constant default value.  This way the value returned upon failure is predictable too and that can occasionally be useful.  IOW, don't return undefined/unpredictable values. HTH. ---------------------------------------------------------------------- Zvoni · 2022-02-15 08:24 Quote from: 440bx on February 15, 2022, 08:51:48 am I also suggest that immediately upon entry, the function set the returned value to some known/constant default value.  This way the value returned upon failure is predictable too and that can occasionally be useful.  IOW, don't return undefined/unpredictable values. HTH. Agreed. Usually, my first line after the "Begin" is to set the Return-Value to Failure! That way if something goes wrong i can just call an "Exit". I admit though, that i'm not a friend of "premature" function-exits. I always try to let the function run its course ---------------------------------------------------------------------- jollytall · 2022-02-15 18:04 Thanks. I also use option 3 a lot, but it has to serious drawbacks. One I mentioned above, i.e. that even the simplest code becomes complicated. Instead of Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ax := 3;bx := 4;c := f(ax) + f(bx);we have to declare two more variables and a longer code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ax := 3;bx := 4;f(ax, ay);f(bx, by);c := ay + by;This is 66% more codes plus the variable declarations, so in total about double. The other problem that this approach is only doable in the classic way, but not in OOP, e.g. if the function is to set a property of an object. There the result of the function must be the actual return value, not an error code. So, only 1,2,5, and 7 can work. ---------------------------------------------------------------------- 440bx · 2022-02-15 18:38 Quote from: jollytall on February 15, 2022, 07:04:15 pmThanks. I also use option 3 a lot, but it has to serious drawbacks. One I mentioned above, i.e. that even the simplest code becomes complicated. Instead of Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ax := 3;bx := 4;c := f(ax) + f(bx);we have to declare two more variables and a longer code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ax := 3;bx := 4;f(ax, ay);f(bx, by);c := ay + by;This is 66% more codes plus the variable declarations, so in total about double. The other problem that this approach is only doable in the classic way, but not in OOP, e.g. if the function is to set a property of an object. There the result of the function must be the actual return value, not an error code. So, only 1,2,5, and 7 can work. But there are very significant problems with the code you showed, in particular the lack of error checking.  for what you showed, I'd do something along these lines:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ax := 3;bx := 4;if f(ax, ay) and f(bx, by) then c := ay + by else { report/handle an error }; ---------------------------------------------------------------------- Warfley · 2022-02-15 20:35 I think generally you can categorize these things into 3 categories: 1. Return the error through an external channel, your points 1 and 7 2. Return the error as regular function result, this is your points 2,3,4 and 6 3. Notifying about the error via raising exceptions, your point 5, So first I think it is useful to discuss what an error actually is, because not all errors are equal. I don't even like the word error, I think more fitting is the term Exception, because an exception is when an unexpected state occured. An error is a subset of exceptions, where this state results in errorneous computations. So for example, if in a Websockets connection one of the parties disconnects without sending a close message, this surely is not normal behavior, but the standard explicitly allows this and it should not throw an error but basically handle this as if the client terminated gracefully. This is an exception that is not an error. Because the term exception is also the name of the language concept, I will stick to error, but it should be kept in mind that not all errors are actual errors. Some can indicate fine behavior. The second question is if the user needs to act in case of an error. In the websockets example above, the user does not need to be notified about this form of closing, as it can be transparently handled the same way a graceful close is handled, this is basically what the standard says. Another example, this time of a true error would be a logger. If you want to log to a file, but you have no access rights to the file, you could design your logger such that it just ignores the error, and just does not do logging. This for example could be done if you are writing a server that might or might not be connected to a terminal, and you want to write to the terminal if it exists but simply do nothing. Lastly, there is also the question on how much errors are expected. For example when parsing user input, you expect errors to happen, because users tend to mistype a lot. And also the reaction to this is usally very simple to just ask again. On the other hand, if you parse a computer generated string, you usually do not expect any parsing errors, and if you encounter one, this is probably a breach of any fundamental assumption (e.g. structure of the input, data source, etc.) and usually requires a complete stop to the operation to reevaluate what gone wrong. The second aspect is one of system design, because different systems incentivise different behavior. An opt-in mechanism will lead to a preference of not using a mechanism, while opt-out will lead to being more likely to use that one. For example, when Apple changed the default behavior of iOS from app specific tracking opt-out (you where tracked until you went to the options and deactivated it) to opt-in (when using an app that wants to track your behavior you get a popup asking you to accept it), suddenly a lot more people did not want to be tracked, which resulted in a hughe loss on the parts of Facebook, who rely on such data. The thing is, the users haven't changed with the update, it's just the incentive through design choices. The same is applicable to language design. If you have an opt-in solution, it will be used less than an opt out solution. Taking for example #4 this is an opt out. To ignore the error you need to actively create a dummy variable which you ignore. On the other hand exceptions are opt-in, as you actively must add the try-except block to handle the error. The last one is also about system design, but with respect of documentation. There is this saying in application design, that every time the user has to consult the manual, it's a failure of design. This is similar for code. Every time the user has to consult the documentation, the code failed at being easy to understand. As a nice example for this, take the function TryStrToInt(String, out Integer): Boolean. I don't need to consult the documentation to understand that it tries to convert a string to an integer, with the result being if the try was successful and the result will probably be in the out parameter. On the other hand take the Sockets API handling of non blocking reads. So when doing a non blocking read, you will read everything that was buffered up to this point. The result will then say how much was actually read. So what if the buffer is empty, the logical thing would be to return 0... Nope it is -1 indicating an error and errno says EWouldBlock (or EAgain because fuck consistency). So what about if the client disconnected, if I am trying to recieve from a disconnected client, it surely will throw an error... Nope thats 0. This behavior has historic roots, as non blocking sockets where added later, but it is still a great example for terrible design. When using the sockets API, you will be looking at the documentation a lot. The socket API is just terribly designed. So now looking at the three methods. The first is I think the worst, in the sense that it has it's use cases, but they are so seldom that no one would miss it if it wasn't used at all. This is an opt-in mechanism. And because it uses a separate channel, that is not directly linked with the specific function or error, you always need to look into the documentation. This will basically result in it being probably mostly ignored. Take a look at the following code from the FCL-Net library, which is shipped with the fpc: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   fpsendto(sock,@qry,qrylen+12,0,@SA,SizeOf(SA));  // Wait for answer.  RTO:=TimeOutS*1000+TimeOutMS;  fpFD_ZERO(ReadFDS);  fpFD_Set(sock,readfds);  if fpSelect(Sock+1,@readfds,Nil,Nil,RTO)<=0 then    begin    fpclose(Sock);    exit;    end;  AL:=SizeOf(SA);  L:=fprecvfrom(Sock,@ans,SizeOf(Ans),0,@SA,@AL); Here no error checking is performed, it would require a check if the result of fpsendto = qrylen+12 and if not errno needs to be checked to see whats wrong. Most importantly, if the sending fails, it will not receive anything, therefore waiting until the timeout is reache, which is 5 seconds. So on a network error, this function will just freeze for 5 seconds before return with the error (that said, the error handling of the netdb is terrible anyway, there is no way to find out what kind of error there was, if it was a timeout, a network error, whatever, it will just return nothing as a result if it failed. In fact the netdb library being so bad w.r.t. error handling was the reason why I completely rewrote it from the ground for my STAX AsyncNet library. This is one of the reasons why I thought so much about error handling and why I have so much to say about it). This should not happen, and the design of the sockets API should enforce that this does not happen. So what would be the better solution here? Well one can argue that errors during sending are rather rare (at least on modern hardware with giant buffer sizes and fast networks), but when something happens, you basically want to crash the whole function. For this exceptions are great. While through them being opt-in, it is still easy to forgett using try-except, but even though you forgott it, when an exception arises, it will just stop the function relying on that functionality and crashing the program. Because if something fails at this stage, something went horribly wrong. I think the first use case is only than a good solution, if the channel through which the error is given is at least somewhat related to the cause (e.g. when it is the method of an object GetError or similar, for getting the errors encountered by that object), and if the default behavio should be to ignore the error, and this should be fine. Basically the websockets or logging example from above. In all other situations this is a bad option. Also even in those situations, it is arguable that other solutions aren't that much worse. I would say simple conduct, do not use this at all, because misusing it is easier than using it correctly. With the 2 and 3, i.e. as some form of function result or as raising an exception, the choice here is I think mostly dependent on the expectedness of the errors, and the behavior when catching one. Generally speaking, as explained above, exceptions are great if you want as a result of an exception to just stop. So if what you encountered is so wrong that you cannot continue. For everything else you should use some form of function output. For example, the reading from a non blocking socket, and the socket was closed by the other side, this should be an exception, because you cannot read from it. Any function that relied on reading from this socket cannot continue anymore. On the other hand, when there currently is no data to read, this can simply be encoded as the function result of 0 bytes read. This is also an exception, but the function that tries to read can just see this and say: "hey I need more than that" and try again. Another example is assertions about the current state. So for example in my recent STAX project there are a lot of potential pitfalls that the user might not be aware of. For example you cannot schedule a task in to executors at the same time (or switch between them). So while I can write this into the documentation, as I said, if you have to read the documentation but no one reads the documentation carefully. So I simply added the following check: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     else if ATask.Executor <> FExecutor then      raise EForeignTaskException.Create('Can''t await a task from another executor');So when someone tries, it results in an exception which if the user does not know about crashes the program, because what was intendet to do would not have gone well, so a crash is the best option. This is just one example. Pretty much all of my publicly facing function have multiple error ckecs in the beginning to throw exceptions if the preconditions are not met. I of course hope that the user of these functions will never hit these conditions, but if they do, rather than doing something completely wrong, I want to rather have the program crash with an exception. Another thing where exceptions are useful is when having multiple sources of errors with equal results, for example when doing networking, you might find yourself in the situation calling multiple receives, every single one could close the connection, thereby throwing an exception, and if it occurs at any of them, you want to just do something and quit, easy: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i try  Receive(...);  // do something  Receive(...);  ...except on E: ESocketException do  // handle error of any of the receivesend;This is much cleaner than having to write the same error handling code on each of the Receives So after writing way to much about this topic, here are a few things about Exceptions that I do not like about in Lazarus. Because I think they are a great tool, as I hopefully explained here, but there are two things I think are really annoying. The first one is that the debugger will break on any exception. You can disable it for a certain type of exception, but not for specific exceptions. Modern IDEs like IntelliJ for Java or VSCode for Python have two options, first to ignore exceptions from a given line. So if you have code that regularly throws an exception, e.g. an EConvertError when parsing user input, but you do not want to ignore all EConvertErrors, you can just say: "Ignore all EConvertErrors originating from this line" and you are fine. Second is to not catch exceptions that will be cought by an except block. Because if there is an except block, it will probably be handled all right (this is a big problem for my STAX, because here exceptions are handed over between tasks, which will throw the same exception multiple times, just to be cought and rethrown at a later point in time). This makes them really annoying to use sometimes, even though they are in concept great. Second is something I really like from Java, that is that functions must say in their declaration which Exceptions they can throw. So if you do not catch the exception, you need to mark the calling function also with that exception so it can be propagated. This makes exception from an opt-in to an opt-out, at least party (just allowing to re- raise is not really handling), but this forces you to at least consider the exceptions that can be thrown. And now, the really last point, about the return value, since we have generics, we can also use an Optional type indicating success: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function StrToIntOpt(const AString: String): TOptional;var  Value: Integer;begin  If TryIntToStr(AString, Value) then    Result := Value  else    Result := EmptyOptional;end; // usagevar  age: TOptional;  str: String;begin  while not age do  begin    Write('Enter Age: ');    ReadLn(Str);    age:= StrToIntOpt(str);  end;  WriteLn('You are: ', Age.Value);end;Which I think is in some cases a cleaner option than having a dedicated out variable (as it allows ignoring it). ---------------------------------------------------------------------- ArtLogi · 2022-02-15 21:24 Does that last example compile, should non declared res be declared age instead.. If these exceptions are objects, isn't it possible then introduce a new property to it "silent" to use ie. global (in desired scope) variable to turn off certain constant exceptions while debugging, or is it inbuild to raise etc reserved words. ---------------------------------------------------------------------- Warfley · 2022-02-15 21:36 Quote from: ArtLogi on February 15, 2022, 10:24:04 pmDoes that last example compile, should non declared res be declared age instead.. Yes thats a typo, fixed it QuoteIf these exceptions are objects, isn't it possible then introduce a new property to it "silent" to use ie. global (in desired scope) variable to turn off certain constant exceptions while debugging, or is it inbuild to raise etc reserved words. Honestly I don't know exactly how debugger integration is with exceptions. On a functional level exceptions (at least on linux) are basically just a longjmp to the except block. How the Debugger is Informed I don't know ---------------------------------------------------------------------- ArtLogi · 2022-02-15 21:57 Quote from: Warfley on February 15, 2022, 10:36:54 pmQuote from: ArtLogi on February 15, 2022, 10:24:04 pmDoes that last example compile, should non declared res be declared age instead.. Yes thats a typo, fixed itPhew, I was worried that there would have been result and res introduced at some point as a short handQuoteQuoteIf these exceptions are objects, isn't it possible then introduce a new property to it "silent" to use ie. global (in desired scope) variable to turn off certain constant exceptions while debugging, or is it inbuild to raise etc reserved words. Honestly I don't know exactly how debugger integration is with exceptions. On a functional level exceptions (at least on linux) are basically just a longjmp to the except block. How the Debugger is Informed I don't know You do know  much more than I do then.  I do just have wild hypotheses. ---------------------------------------------------------------------- Kays · 2022-02-15 22:45 Quote from: jollytall on February 14, 2022, 09:20:37 pm[…] how to handle an error in a method (procedure or function)? […]Terminology: A method is a routine belonging to an object or class. Quote from: jollytall on February 14, 2022, 09:20:37 pmImagine a mathematical function: f(a,b,c) = sqrt(a/(b-c)). If b=c or a/(b-c) is negative, the function shall fail somehow.Err, it shall not⁈ Expand your codomain to complex numbers? I mean seriously, good design should not produce any errors in the first place. Now, a division by zero is a tricky thing, but (if it is suitable for the application) the x87 fdiv instruction does return ±∞ if the exZeroDivide exception is masked. PS: You missed one option: “For […condition…] the behavior is undefined.” ---------------------------------------------------------------------- jollytall · 2022-02-16 07:45 Quote from: Kays on February 15, 2022, 11:45:46 pmTerminology: A method is a routine belonging to an object or class. So, what is the common name covering both function and procedure? Officially, I think, they are called subprograms, but I have never seen it used. Is it routine? Quote from: Kays on February 15, 2022, 11:45:46 pmErr, it shall not⁈ Expand your codomain to complex numbers? Actually, I though about it when I wrote it, thinking that someone will be so precise to comment on it, but I hoped that the intention is clear without over-complicating even the question. Quote from: Kays on February 15, 2022, 11:45:46 pmI mean seriously, good design should not produce any errors in the first place. The whole question assumes you write good code that does not create run-time errors. That is a starting point. So the "routine" makes sure it does not crash. The question how to inform the caller (what can even be written by someone else) that with those inputs he gave the task cannot be executed? ---------------------------------------------------------------------- dseligo · 2022-02-16 08:07 Quote from: jollytall on February 16, 2022, 08:45:57 amSo, what is the common name covering both function and procedure? Officially, I think, they are called subprograms, but I have never seen it used. Is it routine? Routine: https://wiki.freepascal.org/Routine ---------------------------------------------------------------------- jollytall · 2022-02-16 08:16 Quote from: Warfley on February 15, 2022, 09:35:16 pmI think generally you can categorize... Thanks for your detailed thoughts. Option 1 (global error variable) I do not like and use it either on my own (only if the library I call makes me use it). Option 7 (you do not detail) I use sometimes, when I write the main program as well as all the "important" units (libraries) of it and hence can make an overall logging, error handling unit. But, clearly that is not easy when one only writes pieces of a software. Option 5 and 6 (raising an exception) (I do not see why you moved option 6 to the second category): I agree with your concern re the use of raise when using a debugger. It is annoying, although you can continue, so to me this is not a major problem. I am not convinced though that exceptions should only be raised if the error is so severe that the whole execution shall be stopped. Theoretically, it can also be a good mechanism, to inform the caller about any unexpected operation. The main problem I see is that no result value of the function can be used in combination with a raise, so it must be a var parameter. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  d : double; function Temperature1 : double;  begin  // we measure the temperature from a thermometer here, and it is 12.0  result := 12.0;  raise Exception.Create('Room too cold (1)');  end; procedure Temperature2(var res : double);  begin  res := 12.0;  raise Exception.Create('Room too cold (2)');  end; begin try  d := Temperature1;except  on E:Exception do    begin    writeln(E.Message);    writeln('T1= ',d);    end;  end; try  Temperature2(d);except  on E:Exception do    begin    writeln(E.Message);    writeln('T2= ',d);    end;  end; readln;end. This gives the caller the flexibility to take the exception seriously or not. (Btw. I know, you can say, that a Temperature function should tell the temperature and the caller can decide if it is too cold or not, but that is not the point of the above example.) Also, if we are sure that no exception will be raised (q := sqrt(2) will not raise an out of range error for sure), then we do not need to put it in a try..except (or try..finally) block, so it can simplify things. Last, my option 6, also gives that flexibility that the caller can say in advance that I am not interested in exceptions. See my earlier example about a SplitString procedure. In some cases It is critical if there is no ':' in the input string (incorrect input, must be handled) and so an exception raised is a good thing. In some other cases it is normal not to have it, so we do not even want to bother with the exception, so we set the extra parameter to false. ---------------------------------------------------------------------- jollytall · 2022-02-16 08:19 Quote from: dseligo on February 16, 2022, 09:07:00 amRoutine: https://wiki.freepascal.org/Routine Thanks for this link. Strangly enough pages like https://wiki.freepascal.org/Basic_Pascal_Tutorial/Chapter_4/Procedures and https://www.tutorialspoint.com/pascal/pascal_functions.htm do not once mention "routine". They call them subprograms... ---------------------------------------------------------------------- Warfley · 2022-02-16 09:26 Quote from: jollytall on February 16, 2022, 09:16:28 amI am not convinced though that exceptions should only be raised if the error is so severe that the whole execution shall be stopped. Theoretically, it can also be a good mechanism, to inform the caller about any unexpected operation. The main problem I see is that no result value of the function can be used in combination with a raise, so it must be a var parameter. This gives the caller the flexibility to take the exception seriously or not. (Btw. I know, you can say, that a Temperature function should tell the temperature and the caller can decide if it is too cold or not, but that is not the point of the above example.) Also, if we are sure that no exception will be raised (q := sqrt(2) will not raise an out of range error for sure), then we do not need to put it in a try..except (or try..finally) block, so it can simplify things. With the whole whole execution shall be stopped I meant when it is unhandeld. This is due to their design, when you raise an exception, and there is no try-except, it will crash the program. And as try- except is an opt-in, the default behavior of most programmers is to not use it until it's needed. So for the programmer the fact that an exception arises often implies that something really bad has happend. I remember a thread here in this forum where the HTTPClient threw an ConnectionClosed exception, which is completely normal behavior in HTTP, that a keepalive connection can at any point be killed by the server (e.g. due to load), so if that happens the client simply needs to reconnect. The user assumed this was an error because in their mind exception => error,  and I cannot fault them because this is what the design of exceptions imply (after all they crash your program). The most telling about this story is that the solution for the user was not to handle the exception, but to use another component (I think synapse if I remember correctly) that simply does not throw an exception in this case. This might be generally correct behavior, but because it breaks with the general convention and implicit assumptions about exceptions, it requires the user to consult the documentation, which is failed design. So the use of "everythings allright" exceptions is probably more confusing than anything else. If you want to transmit additional information (what you are basically using the exception for in this case), extend your output space, e.g. by rather than returning a sinlge output, return a composite of multiple informations. So in your example, it would probably be more useful to return the information if it's to cold as part of the result: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TTemperatureMeasurement = record    Temp: Double;    To0Cold: Boolean;  end; function Temperature: TTemperatureMeasurement;begin  ...end;Or you could use out parameters to extend the output space (but personally I like return values more, because you can easiely ignore them). Personally I've wrote a Tuple unit with a few generic composite types, which I tend to use for this sort of stuff: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function Temperature: TPair;begin  Result.First := 12;  Result.Second := True;end; Second through their design exceptions always stop the current function, so the default assumptions programmers make is that the function probably hasn't finished correctly, so the results it produced are probably not usable. This is the reason why the Result variable is not usable at all, and using a var or out parameter, while being theoretically correct, still breaks this assumption. I think the better solution to give results in the exception case is to write them into the exception. When I needed to do that, I just added a Field "PartialResult" to the exception, that was read in the except block and used as result: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function Temperature: Double;begin  Result := 12;  raise ETooColdException.Create(Result);end; PS: about the procedures and functions, I when talking about both, I call them simply functions. Routine would be the pascallian way to talk about this sort of stuff, but I am actively using a lot of different languages, where there is no distinction and the usualy term is function, so I just use the Term everyone understands, rather than having to juggle with different terms when I am talking about Pascal or when I am talking about other languages ---------------------------------------------------------------------- Warfley · 2022-02-16 09:54 Quote from: Kays on February 15, 2022, 11:45:46 pmI mean seriously, good design should not produce any errors in the first place. Now, a division by zero is a tricky thing, but (if it is suitable for the application) the x87 fdiv instruction does return ±∞ if the exZeroDivide exception is masked. Any program that interfaces with the real world will produce errors. You can create the best XML parser in the world, but if it get's JSON input it will throw an error. There actually is a programming language that had not throwing errors as one of their top priorities, this is Javascript, and this results in these great features like ('b' + 'a' + + 'a' + 'a').ToLower() == 'banana'. Needless to say, today the ECMAScript committee tries everything to fix the problems that this introduced in the past. Also while a program should never produce errors, programmers are not perfect and we all produce bugs. Therefore you should always check you assumptions and throw errors if they are violated. For example in my STAX program I have a  dependency counter. If a dependency arises, it is incremented, if it is resolved, it is decremented. A Task can only finish once all dependencies are resolved, so the counter reaches 0. This *should* not produce an error if everythings allright. That said, I forgott in some cases to increment the counter, while always decrementing it. A classical bug, I just forgott to include one function call within an if, these sort of bugs just happens. Do you know how I found this bug immediately? Every time I touch the counter, I have a check: if counter < 0 then throw an exception. If the program is correct this exception should never fire, but it did, so I immediately knew that something went completely wrong. Not only that I knew exactly where I had to look because of this exception. If I hadn't had the exception the result would be that simply the Task would have finished before all it's dependencies. This wouldn't have caused a crash or similar, it would have just called wrong values in the result, something that could literally be caused by any bug. So you should always check your preconditions and build in error mechanisms to find out if they are violated. ---------------------------------------------------------------------- PascalDragon · 2022-02-16 14:52 Quote from: Warfley on February 15, 2022, 09:35:16 pmSo after writing way to much about this topic, here are a few things about Exceptions that I do not like about in Lazarus. Because I think they are a great tool, as I hopefully explained here, but there are two things I think are really annoying. The first one is that the debugger will break on any exception. You can disable it for a certain type of exception, but not for specific exceptions. Modern IDEs like IntelliJ for Java or VSCode for Python have two options, first to ignore exceptions from a given line. So if you have code that regularly throws an exception, e.g. an EConvertError when parsing user input, but you do not want to ignore all EConvertErrors, you can just say: "Ignore all EConvertErrors originating from this line" and you are fine. Second is to not catch exceptions that will be cought by an except block. Because if there is an except block, it will probably be handled all right (this is a big problem for my STAX, because here exceptions are handed over between tasks, which will throw the same exception multiple times, just to be cought and rethrown at a later point in time). This makes them really annoying to use sometimes, even though they are in concept great. You should report these as feature requests then considering that the debugger is something that is quite a lot worked on currently. Though especially the functionality regarding the try … except might be quite hard to implement cause the debugger needs to have quite some insight into the current state of the exception handling mechanism then.