-
Notifications
You must be signed in to change notification settings - Fork 157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[#2141] Use optionals for return values that may be null to enforce a client check #2144
base: master
Are you sure you want to change the base?
[#2141] Use optionals for return values that may be null to enforce a client check #2144
Conversation
abd78d8
to
1d9ce27
Compare
In line with keeping PRs small and working on issues incrementally, I have decided to stop working on the implementation of Future PRs will look into the implementation of |
Just an initial thought: It would be nice if exceptions were part of the Failable optional type (e.g FailableOptional<LocalDateTime, DateTimeParseException>) which indicates that there exists the possibility of a throw which is still unhandled. Composing FailableOptionals with different unhandled exceptions should produce a FailableOptional with the sum type of these exceptions (this may be hard to do with Java 8/11). Handling the error, then gets rid of the exception from the type. Inspiration for monads of this kind is from Scala Zio and effect-ts. |
@gok99 Reflective access was previously required to ensure that the functions that users define can only throw the declared type or subtype of exceptions. However, upon further testing, I realised that the function isn't working as intended; all exceptions are instances of Throwable and hence no exceptions are thrown. For now, flatmap requires users to not throw incompatible types of exception in the methods. Union types are not available in Java 11 (union types are only available in Java 17 through the sealed interface feature, but even then it does not offer the same kind of type unionisation that we need for our use case), and using tuple types seems viable for maintaining type safety, but it does seem rather clunky and does not appear to offer type safety if you wish to obtain a stored value. I could explore it if you wish, and report back with any findings that I may have. But in the meantime, perhaps we could restrict flatmap to only map to the same exception type, and require users to explicitly define a new exception type if necessary? |
…hjkxd/RepoSense into enhancements-use-optionals
As discussed, Java is probably not well equipped for these kinds of type gymnastics. Let's instead (of paramaterizing with exceptions) require that throwing optionals are given default handlers (that can be dynamically rebound later) as a compromise. |
Hi, |
Hi, |
Hi, |
This PR was closed because it has been marked as stale for 7 days with no activity. |
Part of #2141
Proposed commit message
Other information
Main Rationale Behind
FailableOptional
MonadCurrently, there are chunks of code whereby
null
s are used as a flag or are part of a process whereby exceptions are thrown or messages are logged. The built-in JavaOptional
class (in Java 8) does not support some of the operations or require lengthy workarounds to replicate the behaviour we want directly usingOptional
.For example, consider
CommitInfoAnalyzer::analyzeCommit
:Using
null
in this manner is dangerous ifDateTimeParseException
is thrown, sincedate
would have the valuenull
, and the subsequent invocation ofdate.withZoneSameInstant
would fail due to aNullPointerException
.Optional
could be used here, but it is rather clunky becauseOptional
's interface does not handle the production of values that might throw exceptions. Moreover, the other mapping and filtering methods do not allow lambdas or methods which throw exceptions.The use of
FailableOptional
can help solve this, while mitigating the risk of invoking methods onnull
, by wrapping up the function call that potentially throws an exception, and working with it as if it had not thrown, shielding the user from the complexity of handling thenull
values.Consider this rewritten chunk of code:
This following implementation would not be subjected to the above buggy behaviour, as the potentially throwing method is wrapped into a
Fail
-ed instance ofFailable
, and subsequently recovered into a valid defaultLocalDateTime
value.So far,
Failable
supports the usual operations thatOptional
can do (identity (of
,ofNullable
), mapping (map
,flatmap
) and filtering (filter
)), and support new operations that might be useful in our codebase (handling methods that might throw exceptions when executed but otherwise returns a value, etc.).So far, the following methods have been implemented:
Identity
of
ofNullable
success
empty
fail
isPresent
isAbsent
isFailed
Mapping
map
unfailableMap
flatMap
unfailableFlatMap
Filtering
filter
Getters
get
orElse
Misc. methods
ifPresent
ifAbsent
ifFail
resolve
ifFailThenThrow
failWith
Here are some of the main processes that we need to complete to close out this Issue/PR. The relevant PRs which fulfil the tasks are included in the parenthesis following the task name below:
Failable
monad (#2144)Failable
(#2144)Failable
can be used (#2144)Failable
in key areas identified (#2144)