Adam Cooper
11/07/2022, 10:39 PMResult
than throw an exception. Is that true in all cases? I've found from my experience in other languages that it's a lot easier to write a REST API with some type of special HTTP status exception that you can throw, which will be handled at the top level and respond appropriately to the client. Should libraries use exceptions? I haven't seen any libraries which return `Result`s.Joffrey
11/07/2022, 10:45 PMResult
is not meant for railway programming, as stated in the corresponding KEEP. I personally prefer using exceptions in general, and particularly for unexpected failures, with an occasional custom result types in specific cases where it makes sense to expect some type of errors.
But I'd still say it's a personal preference. Do you have the link to the article by any chance? I'd be interested.Adam Cooper
11/07/2022, 10:47 PMAdam Cooper
11/07/2022, 10:47 PMAdam Cooper
11/07/2022, 10:49 PMrequire
, etc.) for validation, since those throw exceptions as wellAdam Cooper
11/07/2022, 10:50 PMResult
type. and i think it's fine to throw exceptions in situations where things haven't gone horribly wrong.Joffrey
11/07/2022, 10:50 PMrunCatching
in general application code, though. You can see my answer here about that: https://stackoverflow.com/a/70847760/1540818Adam Cooper
11/07/2022, 10:55 PMJacob
11/08/2022, 3:34 AMJacob
11/08/2022, 3:36 AMJoffrey
11/08/2022, 1:52 PMJoffrey
11/08/2022, 1:57 PMChris Fillmore
11/09/2022, 4:25 PMResult
type, which I can control. I don’t use kotlin.Result
. It’s similar but in particular the Failure
is more tailored to my application’s needs.
If I recall correctly, Roman also makes the point that you may not want to pollute low-level code with results. The result idiom is perhaps more application- or business-centric.
Since you mentioned require
, it’s worth pointing out that this checks invariants in your system. The reason you want this to throw is that, if require
fails, it means your system is in an invalid state and it may be dangerous to continue (e.g. by causing data corruption).
This is different from expected “errors” like:
• a resource could not be found
• user is not authorized
• a hardware interface is unavailable
• etc
To be clear I’m not trying to promote exceptions vs results here. I tend to only use exceptions when programming against an external API (e.g. OkHttp Interceptor), and results elsewhere.Adam Cooper
11/09/2022, 4:31 PMrequire
throws IllegalArgumentException
. I thought check
(IllegalStateException
) describes more what you're talking about with regards to system invariantsJoffrey
11/09/2022, 4:32 PMChris Fillmore
11/09/2022, 4:33 PMAdam Cooper
11/09/2022, 4:34 PMResult
obviously, but should I use a custom Exception
type?Chris Fillmore
11/09/2022, 4:35 PMrequire(arg in lowerBound..upperBound) { "Expected arg to be in range ... but found $arg" }
Adam Cooper
11/09/2022, 4:36 PMJoffrey
11/09/2022, 4:36 PMrequire
is good in a constructor, if you're checking arguments of courseChris Fillmore
11/09/2022, 4:36 PMAdam Cooper
11/09/2022, 4:38 PMinvariant
which essentially meant "crash now because something is very wrong"Chris Fillmore
11/09/2022, 4:38 PMJacob
11/09/2022, 4:39 PMchecks
to any functions that need the properties to be valid. Making constructors too smart can get painful.Joffrey
11/09/2022, 4:40 PMJoffrey
11/09/2022, 4:41 PMAdam Cooper
11/09/2022, 4:43 PMThis is why technically checking an invariant shouldn't be necessary in working code, but we do it to help finding bugs.Agreed! My constructor can consume "user" data though (it's a library for RSS). If it gets a bad RSS feed, would that count as an invariant? The program using the library could obviously continue on, it would just have to reject the feed
Chris Fillmore
11/09/2022, 4:43 PMChris Fillmore
11/09/2022, 4:43 PMChris Fillmore
11/09/2022, 4:44 PMAdam Cooper
11/09/2022, 4:44 PMskipHours
tag, which describes which hours the feed cannot be refreshed. So if they input an hour that is not a valid hour of the day (between 0 and 23), then it is invalidJoffrey
11/09/2022, 4:45 PMJoffrey
11/09/2022, 4:46 PMChris Fillmore
11/09/2022, 4:46 PMChris Fillmore
11/09/2022, 4:47 PMJoffrey
11/09/2022, 4:47 PMChris Fillmore
11/09/2022, 4:48 PMAdam Cooper
11/09/2022, 4:48 PMTechnically in this case the user didn't respect the documentation's contract, and it's an illegal argument, so in any case IMO throwing IAE is the correct thing to doGood point!
Ah ok interesting. Yeah so this occurs at a boundary of your program right? i.e. you are getting data from an external feed.It depends on what the programmer wants to use the library for. I've broken it up into 3 modules: `core`: Contains basic data classes for serialization/de-serialization of RSS feeds into an object `client`: Utilities for consuming RSS `server`: Utilities for producing RSS it's meant to run alongside ktor, but in theory it doesn't have to
Adam Cooper
11/09/2022, 4:49 PMcore
. It is sounding like you both agree that a custom exception is a better choice hereChris Fillmore
11/09/2022, 4:57 PMAdam Cooper
11/09/2022, 5:10 PMException
is definitely the move, but I was using require
for that after reading the article I linked aboveAdam Cooper
11/09/2022, 5:11 PMChris Fillmore
11/09/2022, 6:32 PMrequire
for this, because you’re evaluating input at the boundary of your program. Some input is bound to be invalid (consider e.g. user input from a keyboard or whatever).
Good luck with your library!Adam Cooper
11/09/2022, 6:34 PMGeorge
11/13/2022, 3:13 PMMy constructor can consume "user" data though (it's a library for RSS). If it gets a bad RSS feed, would that count as an invariant?Yes i would count it as invariant/logic-error since it depends on user input. Whether user needs special knowledge to provide the proper input or not. i would recommend using IAE, but it always depends on the case. If your exception should contain info more than a simple message ("input: $field is not compliant with spec #1234"), then i would recommend building a custom exception which extends IAE where the consumer can still catch your exception in the same top-level handler. Speculation: I think
kotlinx.serialization
follows similar approach.