Better solutions to long pattern matching chains (for Dart) - dart

I've often found myself having to use annoying patterns like:
ClassA extends BaseClass {
static bool is(val) { ... }
}
ClassB extends BaseClass {
static bool is(val) { ... }
}
...
ClassZ extends BaseClass {
static bool is(val) { ... }
}
BaseClass parser(val) {
if(ClassA.is(val)) {
return ClassA(val);
} else if(ClassB.is(val)) {
return ClassB(val);
...
} else if(ClassZ.is(val)) {
return ClassB(val);
}
}
This is very error prone and requires a lot of monotonous code.
I was wondering if there was a way to expedite this process in a non-language specific (or in a language specific for Dart) way that doesn't involve listing all the pattern matchers after they've been defined. I would like to avoid this as I had too many bugs to count caused by forgetting to list one of the already defined class's pattern matcher.

If you want to cut down the arbitrary conditionals in the BaseClass.parser(), you can use a map as follows:
typedef Specification = bool Function(dynamic val);
typedef Factory = BaseClass Function(dynamic val);
class BaseClass
{
static final Map<Specification, Factory> _factoryMap = {
(val) => val == 'Hello': (val) => ClassA(),
(val) => val == 'There': (val) => ClassB(),
};
static BaseClass? parse(dynamic val)
{
for(var key in _factoryMap.keys)
{
if(key(val)) return _factoryMap[key]!.call(val);
}
throw ArgumentError('No valid factory found!');
}
}
class ClassA extends BaseClass
{ }
class ClassB extends BaseClass
{ }
class ClassC extends BaseClass
{ }
In Python, you can extend type and register the subclass's own specification method without manually listing like this. But I am not aware of such runtime meta programming in Dart for the time being. Maybe you can use source_gen in Dart to generate the conditionals automatically.

Combining the check with the construction of the object would help slightly. That would reduce the potential of accidentally using the wrong check and constructor, and it would reduce the amount of code you'd need to add outside of the class definitions:
ClassA extends BaseClass {
/// Attempts to return a [ClassA] if possible.
///
/// Returns `null` if inappropriate.
static ClassA? tryFrom(dynamic val) { ... }
}
ClassB extends BaseClass {
static ClassB? tryFrom(dynamic val) { ... }
}
...
ClassZ extends BaseClass {
static ClassZ? tryFrom(dynamic val) { ... }
}
BaseClass parser(dynamic val) {
BaseClass object = ClassA.tryFrom(val) ??
ClassB.tryFrom(val) ??
... ??
ClassZ.tryFrom(val);
if (object == null) {
// Throw some exception here.
}
return object;
}
from there, you can make parser use a loop:
typedef TryFromFunction = BaseClass? Function(dynamic);
final tryFromFunctions = [
ClassA.tryFrom,
ClassB.tryFrom,
...
ClassZ.tryFrom,
];
BaseClass parser(dynamic val) {
for (var tryFrom in tryFromFunctions) {
var object = tryFrom(val);
if (object != null) {
return object;
}
}
// Throw some exception here.
}
That wouldn't absolve you of the responsibility of updating some other location whenever a new class is added, but:
The work would be minimal.
You maybe could add unit tests to check that the list is updated. For example, if each of ClassA, ClassB, ..., ClassZ is in a separate file easily distinguished by path or filename, then you could have a test that verifies that tryFromFunctions.length matches the number of those files.

For now, I've used a solution that takes from the others listed and adds a twist. Basically, I create a list of matchers like the others but do the work of adding the matcher in the class itself. Basically,
List<BaseClass Function(dynamic)> _matchers = [];
bool addMatcher(bool Function(dynamic) matcher, BaseClass Function(dynamic) factory) {
_matchers.add((val) {
return matcher(val) ? factory(val) : null;
});
return true;
}
class ClassA extends BaseClass {
static final _ = addMatcher(matches, (val) => ClassA(val));
ClassA(val) { ... }
static bool matches(val) { ... }
}
class ClassB extends BaseClass {
static final _ = addMatcher(...);
...
}
...
BaseClass parser(val) {
for (var matcher in _matchers) {
var object = matcher(val);
if (object != null) {
return object;
}
}
// Throw some exception here.
}
The rational behind this is that I can easily verify that the class is being checked. Unfortunately I'm still not sure how to do this automatically since this was just a quick and dirty solution. I may come back to this with an update if I end up implementing source generation or unit tests for automatic generation/verification.

Related

Conditional type checking of Dart does not work as expected [duplicate]

This question already has an answer here:
Smart cast (automatic type promotion) using 'is' is not working
(1 answer)
Closed 1 year ago.
I have two related classes: Animal and Cat (which extends Animal) then I make an instance of Cat and check if the instance is type Cat so I set amount of paws to four.
abstract class Animal {
}
class Cat extends Animal {
int? pawsAmount;
}
void main(List<String> arguments) async {
Animal barsik = Cat();
if (barsik is Cat) {
barsik.pawsAmount = 4;
}
}
and it works well BUT the follow code is not:
abstract class Animal {
}
class Cat extends Animal {
int? pawsAmount;
}
class Consumer {
Animal _animal;
Consumer(Animal animal) : _animal = animal;
void init() {
if (_animal is Cat) {
_animal.pawsAmount = 4;
}
}
}
void main(List<String> arguments) async {
Animal barsik = Cat();
if (barsik is Cat) {
barsik.pawsAmount = 4;
}
final consumer = Consumer(barsik);
consumer.init();
}
it has got an error:
bin/constest.dart:17:15: Error: The setter 'pawsAmount' isn't defined for the class 'Animal'.
- 'Animal' is from 'bin/constest.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'pawsAmount'.
_animal.pawsAmount = 4;
^^^^^^^^^^
if I change
if (_animal is Cat) {
_animal.pawsAmount = 4;
}
to
if (_animal is Cat) {
(_animal as Cat).pawsAmount = 4;
}
it works fine
Is there any way to make the code work?
Thank you!
Dart only promotes local variables.
The _animal is an instance variable, which means that it can potentially change value at any time (the compiler won't start trying to predict whether it actually does), and therefore you can't promote it - the value might change between the check and the later use, making the check an insufficient guarantee.
I would move the value into a local variable before checking it:
void init() {
var animal = _animal;
if (animal is Cat) {
animal.pawsAmount = 4;
}
}

Calling Generic Types function

is it possible to call generic types function.
if not is there a different approach to something like this.
someFunction<T>(){
T.anotherFunction();
}
EDIT
MyModel model = NetworkClient.sendRequest<MyModel>(url);
static Future<T> sendRequest<T>(String URL){
//send request
var res = data.toString();
return T.fromJson(json.decode(res))
}
void main() {
someFunction(Foo());
someFunction(Bar());
}
someFunction<T>(T t) {
if (t is Foo)
t.fooFunc();
else if (t is Bar)
t.barFunc();
else
throw Exception("Unknown type: ${t.runtimeType}");
}
class Foo {
void fooFunc() {
print("foo");
}
}
class Bar {
void barFunc() {
print("bar");
}
}
Put your method (lets say MyMethod) in an interface or base class. Lets say MyInterface.
Then use a constraint on your generic type:
someFunction<T extends MyInterface>(T t){
t.MyMethod();
}
Since you cannot have constructors in an interface, your plan with the fromJson might not work out. I don't know your class structure. But you can write generics with specific constraints in mind.

Instantiation of object in Vala generic

I want to create a new object of given type inside of generic in Vala language.
class MyClass <T> : GLib.Object
{
protected T data;
public MyClass ()
{
data = new T ();
}
}
I understand that this can't work, but what is the way to do something like that?
You are probably best instantiating it when calling the constructor for MyClass:
void main () {
new MyClass<Test> (new Test ());
new MyClass<Example> (new Example ());
}
class MyClass <T>
{
protected T data;
public MyClass (T data)
{
this.data = data;
}
}
class Test {}
class Example {}
Vala generics do not currently provide constraints. If you are going to pass in a dependency in this way you may want to consider using an interface type instead of a generic type.
Update
If you are wanting to implement a factory then an interface with a static method or function is probably best:
void main () {
var a = CommandFactory.get_command ("A");
var b = CommandFactory.get_command ("B");
a.run ();
b.run ();
}
namespace CommandFactory {
Command get_command (string criteria) {
Command result = null;
switch (criteria) {
case "A":
result = new CommandA ();
break;
case "B":
result = new CommandB ();
break;
default:
assert_not_reached ();
}
return result;
}
}
interface Command:Object {
public abstract void run ();
}
class CommandA:Object, Command {
void run () { print ("A\n"); }
}
class CommandB:Object, Command {
void run () { print ("B\n"); }
}
I assume by 'abstract fabric pattern' you mean 'abstract factory pattern'? You could try using GType introspection to then instantiate the Object, but it must be a GObject and you by pass Vala's static analysis checks:
void main () {
new MyClass<Example> (new Example ());
/* These will fail at runtime
new MyClass<string> ("this will fail at runtime");
new MyClass<ThisWillFailAtRuntime> (new ThisWillFailAtRuntime ());
*/
}
class MyClass <T>
{
protected T data;
public MyClass (T data)
{
assert (typeof(T).is_object());
this.data = Object.new (typeof(T));
}
}
class Example:Object {}
class ThisWillFailAtRuntime {}
Note that Object.new() is also a static method.
I'm not sure what you are trying to achieve, but you are probably better looking more closely at interfaces and favouring composition over inheritance in your object data model.

Override method in dart on fly (like JAVA)

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.
});

Creating an instance of a generic type in DART

I was wondering if is possible to create an instance of a generic type in Dart. In other languages like Java you could work around this using reflection, but I'm not sure if this is possible in Dart.
I have this class:
class GenericController <T extends RequestHandler> {
void processRequest() {
T t = new T(); // ERROR
}
}
I tried mezonis approach with the Activator and it works. But it is an expensive approach as it uses mirrors, which requires you to use "mirrorsUsed" if you don't want to have a 2-4MB js file.
This morning I had the idea to use a generic typedef as generator and thus get rid of reflection:
You define a method type like this: (Add params if necessary)
typedef S ItemCreator<S>();
or even better:
typedef ItemCreator<S> = S Function();
Then in the class that needs to create the new instances:
class PagedListData<T>{
...
ItemCreator<T> creator;
PagedListData(ItemCreator<T> this.creator) {
}
void performMagic() {
T item = creator();
...
}
}
Then you can instantiate the PagedList like this:
PagedListData<UserListItem> users
= new PagedListData<UserListItem>(()=> new UserListItem());
You don't lose the advantage of using generic because at declaration time you need to provide the target class anyway, so defining the creator method doesn't hurt.
You can use similar code:
import "dart:mirrors";
void main() {
var controller = new GenericController<Foo>();
controller.processRequest();
}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T();
T t = Activator.createInstance(T);
t.tellAboutHimself();
}
}
class Foo extends RequestHandler {
void tellAboutHimself() {
print("Hello, I am 'Foo'");
}
}
abstract class RequestHandler {
void tellAboutHimself();
}
class Activator {
static createInstance(Type type, [Symbol constructor, List
arguments, Map<Symbol, dynamic> namedArguments]) {
if (type == null) {
throw new ArgumentError("type: $type");
}
if (constructor == null) {
constructor = const Symbol("");
}
if (arguments == null) {
arguments = const [];
}
var typeMirror = reflectType(type);
if (typeMirror is ClassMirror) {
return typeMirror.newInstance(constructor, arguments,
namedArguments).reflectee;
} else {
throw new ArgumentError("Cannot create the instance of the type '$type'.");
}
}
}
I don't know if this is still useful to anyone. But I have found an easy workaround. In the function you want to initialize the type T, pass an extra argument of type T Function(). This function should return an instance of T. Now whenever you want to create object of T, call the function.
class foo<T> {
void foo(T Function() creator) {
final t = creator();
// use t
}
}
P.S. inspired by Patrick's answer
2022 answer
Just came across this problem and found out that although instantiating using T() is still not possible, you can get the constructor of an object easier with SomeClass.new in dart>=2.15.
So what you could do is:
class MyClass<T> {
final T Function() creator;
MyClass(this.creator);
T getGenericInstance() {
return creator();
}
}
and when using it:
final myClass = MyClass<SomeOtherClass>(SomeOtherClass.new)
Nothing different but looks cleaner imo.
Here's my work around for this sad limitation
class RequestHandler {
static final _constructors = {
RequestHandler: () => RequestHandler(),
RequestHandler2: () => RequestHandler2(),
};
static RequestHandler create(Type type) {
return _constructors[type]();
}
}
class RequestHandler2 extends RequestHandler {}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T(); // ERROR
T t = RequestHandler.create(T);
}
}
test() {
final controller = GenericController<RequestHandler2>();
controller.processRequest();
}
Sorry but as far as I know, a type parameter cannot be used to name a constructor in an instance creation expression in Dart.
Working with FLutter
typedef S ItemCreator<S>();
mixin SharedExtension<T> {
T getSPData(ItemCreator<T> creator) async {
return creator();
}
}
Abc a = sharedObj.getSPData(()=> Abc());
P.S. inspired by Patrick
simple like that.
import 'dart:mirrors';
void main(List<String> args) {
final a = A<B>();
final b1 = a.getInstance();
final b2 = a.getInstance();
print('${b1.value}|${b1.text}|${b1.hashCode}');
print('${b2.value}|${b2.text}|${b2.hashCode}');
}
class A<T extends B> {
static int count = 0;
T getInstance() {
return reflectClass(T).newInstance(
Symbol(''),
['Text ${++count}'],
{Symbol('value'): count},
).reflectee;
}
}
class B {
final int value;
final String text;
B(this.text, {required this.value});
}
Inspired by Patrick's answer, this is the factory I ended up with.
class ServiceFactory<T> {
static final Map<Type, dynamic> _cache = <String, dynamic>{};
static T getInstance<T>(T Function() creator) {
String typeName = T.toString();
return _cache.putIfAbsent(typeName, () => creator());
}
}
Then I would use it like this.
final authClient = ServiceFactory.getInstance<AuthenticationClient>(() => AuthenticationClient());
Warning: Erik made a very good point in the comment below that the same type name can exist in multiple packages and that will cause issues. As much as I dislike to force the user to pass in a string key (that way it's the consumer's responsibility to ensuring the uniqueness of the type name), that might be the only way.

Resources