Elegant error handling in Dart like Scala's `Try` - scala

A data class in Dart:
import 'package:validate/validate.dart';
class AuthUser {
final String email, token, username, bio, image;
AuthUser(this.email, this.token, this.username, this.bio, this.image) {
Validate.isEmail(this.email);
}
#override
String toString() {
return 'AuthUser{email: $email, token: $token, username: $username, bio: $bio, image: $image}';
}
}
where Validate.isEmail will throws an Error when failed to match:
static void matchesPattern(String input, RegExp pattern,[String message = DEFAULT_MATCHES_PATTERN_EX]) {
if (pattern.hasMatch(input) == false) {
throw new ArgumentError(message);
}
}
static void isEmail(String input,[String message = DEFAULT_MATCHES_PATTERN_EX]) {
matchesPattern(input,new RegExp(PATTERN_EMAIL),message);
}
Now I want to use an elegant way to new this class.
When using Scala, I can use Try(new AuthUser(...)) and patten-matching it.
And in Dart, first I tried RxDart,
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
Observable.just(AuthUser("email", "token", "username", "bio", "img"))
.doOnError((e, s) => print("oh no"))
.listen((e) => print(e.toString()));
});
}
Not work, the test failed for the error(which means RxDart doesn't catch errors at all!!!)
And I want to try Future, failed also.
And I want to use dartz, but I am worried because there is just one maintainer...
Any advice?

If you are OK with using Future what's wrong with this advice: Using Future.sync() to wrap your code? The code will look like this:
void main() {
var f = Future.sync(() {AuthUser("email", "token", "username", "bio", "img"); });
f.then((v) => print("Value: " + v.toString())).catchError((e) => print("Failure: " +e.toString()));
}
The main trick is that Future.sync effectively enables lazy evaluation of the parameter but you have to pass your parameter wrapped in a function. This is actually the same trick Scala compiler does for Try (i.e. for call-by-name parameters) but takes adding a few brackets around.

If you only want the basic functionality of returning either type based on whether an exception occurred or not then you can easily create a utility class such as below.
Otherwise I recommend #SergGr's answer about using Future.sync since it gives you more monadic like pipeline.
void main() {
Try<Error, void> result = Try.it(() => Validate.isEmail("test-example.com"));
if (result is Success) {
print("Good");
} else if (result is Failure) {
print("Error: " + result.exception().toString());
}
}
typedef TryExec<V> = V Function();
abstract class Try<E extends Error, V> {
static Try<E, V> it<E extends Error, V>(TryExec<V> fn) {
try {
return Try.success(fn());
} catch (e) {
return Try.failure(e);
}
}
static Try<E, V> failure<E extends Error, V>(Error e) {
return new Failure(e);
}
static Try<E, V> success<E extends Error, V>(V v) {
return new Success(v);
}
}
class Failure<E extends Error, V> extends Try<E, V> {
final E _e;
Failure(this._e);
E exception() => _e;
}
class Success<E extends Error, V> extends Try<E, V> {
final V _v;
Success(this._v);
V value() => _v;
}

Related

How to invoke a listener from another listener?

I'm using in_app_purchase package and I need to convert/map listener which is listening for List<PurchaseDetails> to another listener as shown below:
class Foo {
Foo() {
InAppPurchase.instance.purchaseStream.listen(_listener);
}
void _listener(List<PurchaseDetails> list) {
// How to pass these ids to `addListener()`
final List<String> ids = list.map((e) => e.productID).toList();
}
void addListener(void Function(List<String>) f) {}
}
This is how I want to use my listener
void main() {
Foo().addListener((List<String> ids) {});
}
Despite what your code comment says, I think what you're really asking for is for the internal _listener to invoke the callback that was previously passed as an argument to addListener (and not for _listener to call addListener directly, which it could just do directly).
Just have addListener save the callback to a member variable and let your internal listener invoke that:
class Foo {
void Function(List<String>)? _listener;
Foo() {
InAppPurchase.instance.purchaseStream.listen(_internalListener);
}
void _internalListener(List<PurchaseDetails> list) {
var listener = _listener;
if (listener == null) {
return;
}
final List<String> ids = list.map((e) => e.productID).toList();
listener(ids);
}
void addListener(void Function(List<String>) f) => _listener = f;
}
If you want callers to be able to call addListener multiple times to register multiple callbacks, you would need to store them in a collection (and provide a mechanism to unregister callbacks):
class Foo {
final _listenerMap = <Object, void Function(List<String>)>{};
Foo() {
InAppPurchase.instance.purchaseStream.listen(_internalListener);
}
void _internalListener(List<PurchaseDetails> list) {
if (_listenerMap.isEmpty) {
return;
}
final List<String> ids = list.map((e) => e.productID).toList();
for (var listener in _listenerMap.values) {
listener(ids);
}
}
Object addListener(void Function(List<String>) f) {
var token = Object();
_listenerMap[token] = f;
return token;
}
void removeListener(Object token) {
_listenerMap.remove(token);
}
}

How to override a Dart method on instantiation? [duplicate]

Is there way to overriding method in Dart like JAVA, for example:
public class A {
public void handleLoad() {
}
}
And when overriding:
A a = new A() {
#Override
public void handleLoad() {
// do some code
}
};
No, Dart does not have anonymous classes. You have to create a class that extends A and instantiate it.
No but it much less useful in Dart because you can just reassign function:
typedef void PrintMsg(msg);
class Printer {
PrintMsg foo = (m) => print(m);
}
main() {
Printer p = new Printer()
..foo('Hello') // Hello
..foo = ((String msg) => print(msg.toUpperCase()))
..foo('Hello'); //HELLO
}
However you will need some extra boilerplate to access instance.
Use type Function:
class A {
final Function h
A(this.h);
void handleLoad(String loadResult) { h(loadResult); }
}
Or
class A {
final Function handleLoad;
A(this.handleLoad);
}
A a = new A((String loadResult){
//do smth.
});

RxJava2: Using Flowable with zipWith

I'm trying to make the following code work me but something is wrong, here is a snippet:
private void myMethod() {
Flowable.fromIterable(cache)
.zipWith(this::doesExist, (record, exist) -> {
// do stuff
return true;
}).subscrib();
}
private Flowable<Boolean> doesExist(CacheRecord record) {
// Do something
return Flowable.just(true);
}
This doesn't compile, any idea?
UPDATE:
Any thoughts about the following snippet:
Flowable.fromIterable(m_cache) //
.flatMapCompletable(cachedStation -> {
return Single.zip(Single.just(cachedStation), doesIssueExist(cachedStation), (record, exist) -> {
System.out.println(cachedStation + ", " + exist);
return true;
}).toCompletable();
}).subscribe();
Your doesExist method requires a CacheRecord as a parameter. But the method reference you have given this::doesExist sends an instance of Subscriber<? super Object> that's why the incompatible type error is showing.
The expanded form of your method is given below.
private void myMethod() {
Flowable.fromIterable(cache)
.zipWith(new Publisher<Object>() {
#Override
public void subscribe(Subscriber<? super Object> s) {
doesExist(s);
}
}, (record, exist) -> {
// do stuff
return true;
}).subscribe();
}
Here, the first parameter to zipWith
new Publisher<Object>() {
#Override
public void subscribe(Subscriber<? super Object> s) {
doesExist(s);
}
}
is what you have shortened as this::doesExist
As you can see the zipWith requires the first parameter a Publisher, and you have created an anonymous Publisher, and in the subscribe method you are calling doesExist(s) by sending the Subscriber<? super Object> s, which is not the required type. Your method reference statement this::doesExist does exactly the above operation and that's why the incompatible type error is shown by the compiler.
If you are trying to zip the Flowable with the flowable returned by doesExist method, you can directly call it, without method reference, by passing a valid CacheRecord object as follows
Flowable.fromIterable(cache)
.zipWith(doesExist(anotherCache), (record, exist) -> {
// do stuff
return true;
}).subscribe();
Note: See method reference for more information
Update: If you are trying to pass the items emitted by fromIterable to doesExist method and get combined result boolean and cacheRecord, then
create a holder class as follows
class CacheRecordResult {
CacheRecord cacheRecord;
boolean isExist;
public CacheRecordResult(CacheRecord cacheRecord, boolean isExist) {
this.cacheRecord = cacheRecord;
this.isExist = isExist;
}
}
Then subscribe to CacheRecordResult as follows
private void myMethod() {
Flowable.fromIterable(cache)
.flatMap(cacheRecord -> doesExist(cacheRecord)
.map(exist -> new CacheRecordResult(cacheRecord, exist)))
.subscribe(cacheRecordResult -> {
CacheRecord cacheRecord = cacheRecordResult.cacheRecord;
boolean isExist = cacheRecordResult.isExist;
});
}

Rx.Net: Chaining subscribers - alternative approach?

How can I re-write this code so that I don't have to chain Subscribers like below? Reason for asking is, this style will limit in an observable depending on another observable due to the style of the code, it can get confusing.
var results = myService
.GetData(accountId) // returns IObservable
.Subscribe(data =>
{
new MyWork().Execute(data) // returns IObservable
.Subscribe(result =>
{
myResults.Add(result);
WriteLine($"Result Id: {result.Id}");
WriteLine($"Result Status: {result.Pass}");
});
});
Added after 1st reply from Peter Bons
Below is the code for MyWork class that has the Execute Method
public class MyWork
{
public virtual IObservable<MyResult> Execute(MyData data)
{
MyResult result = null;
return IsMatch(data)
.Do(isMatch =>
{
if (isMatch)
{
result = new MyResult(1, true);
}
})
.Select(_ => result);
}
public IObservable<bool> IsMatch(MyData data)
{
return true;
}
}
It's really quite simple.
var results =
myService
.GetData(accountId)
.SelectMany(data => new MyWork().Execute(data))
.Subscribe(result =>
{
myResults.Add(result);
Console.WriteLine($"Result Id: {result.Id}");
Console.WriteLine($"Result Status: {result.Pass}");
});
If ever you are subscribing within a subscription then you are doing something wrong. Keep that in mind. There is almost always a way to make it a pure query with a single subscription.
Just to help out with testing, here's the code required to make this a Minimal, Complete, and Verifiable example.
public static class myService
{
public static IObservable<MyData> GetData(int x)
=> Observable.Return(new MyData());
}
public class MyWork
{
public virtual IObservable<MyResult> Execute(MyData data)
{
MyResult result = null;
return IsMatch(data)
.Do(isMatch =>
{
if (isMatch)
{
result = new MyResult() { Id = 1, Pass = true};
}
})
.Select(_ => result);
}
public IObservable<bool> IsMatch(MyData data)
{
return Observable.Return(true);
}
}
public class MyResult
{
public int Id;
public bool Pass;
}
public class MyData { }

How to create a custom exception and handle it in dart

I have written this code to test how custom exceptions are working in the dart.
I'm not getting the desired output could someone explain to me how to handle it??
void main()
{
try
{
throwException();
}
on customException
{
print("custom exception is been obtained");
}
}
throwException()
{
throw new customException('This is my first custom exception');
}
You can look at the Exception part of A Tour of the Dart Language.
The following code works as expected (custom exception has been obtained is displayed in console) :
class CustomException implements Exception {
String cause;
CustomException(this.cause);
}
void main() {
try {
throwException();
} on CustomException {
print("custom exception has been obtained");
}
}
throwException() {
throw new CustomException('This is my first custom exception');
}
You don't need an Exception class if you don't care about the type of Exception. Simply fire an exception like this:
throw ("This is my first general exception");
You can also create an abstract exception.
Inspiration taken from TimeoutException of async package (read the code on Dart API and Dart SDK).
abstract class IMoviesRepoException implements Exception {
const IMoviesRepoException([this.message]);
final String? message;
#override
String toString() {
String result = 'IMoviesRepoExceptionl';
if (message is String) return '$result: $message';
return result;
}
}
class TmdbMoviesRepoException extends IMoviesRepoException {
const TmdbMoviesRepoException([String? message]) : super(message);
}
Try this Simple Custom Exception Example for Beginners
class WithdrawException implements Exception{
String wdExpMsg()=> 'Oops! something went wrong';
}
void main() {
try {
withdrawAmt(400);
}
on WithdrawException{
WithdrawException we=WithdrawException();
print(we.wdExpMsg());
}
finally{
print('Withdraw Amount<500 is not allowed');
}
}
void withdrawAmt(int amt) {
if (amt <= 499) {
throw WithdrawException();
}else{
print('Collect Your Amount=$amt from ATM Machine...');
}
}