Coder Social home page Coder Social logo

Comments (12)

leafpetersen avatar leafpetersen commented on September 23, 2024 3

You can use a typedef to accomplish this. e.g.

typedef Nullable<T> = T?;
typedef NullableString = String?;

void test(Type type) {
  switch(type) {
    case const (Nullable<int>): print("int?");
    case NullableString: print("String?");
    default: print("$type");
  }
}

Note that for the generic typedef, you need to wrap it in const as shown above.

from language.

mraleph avatar mraleph commented on September 23, 2024 3

@lukehutch You should probably consider rewriting this code into something like:

super.value = newValue;
// Asynchronously write the new value through to SharedPreferences
unawaited(switch (newValue) {
  null => _sharedPreferences!.remove(sharedPreferencesKey),
  int() =>  _sharedPreferences!.setInt(sharedPreferencesKey, newValue),
  bool() =>  _sharedPreferences!.setBool(sharedPreferencesKey, newValue),
  double() =>  _sharedPreferences!.setDouble(sharedPreferencesKey, newValue),
  String() =>  _sharedPreferences!.setString(sharedPreferencesKey, newValue),
  List<String>() =>  _sharedPreferences!.setStringList(sharedPreferencesKey, newValue),
  _ => throw Exception('....'),
});

In general dispatching on runtimeType is not something you should be doing.

from language.

lukehutch avatar lukehutch commented on September 23, 2024 3

@munificent @mraleph @leafpetersen @mateusfccp Thank you so much for stepping me through all of this. You guys are amazing. I haven't come across another development community before that was so committed to answering questions in helpful ways. Much appreciated.

@munificent I hadn't upgraded to Dart 3.0 previously, but I have now, and I love the new features.

from language.

mateusfccp avatar mateusfccp commented on September 23, 2024 1

...so to accomplish this, I need to change switch into a series of if-statements of the form if (type is int?) {...}.

This won't work anyway. type is int? will ever fail, because type is actually [sic] Type. If you try to check type == int?, it will also raise an error.

The only way to check in a if is to use a helper function Type typeOf<T>() => T and check for if (type == typeOf<int?>()).

With a switch, however, it won't work, as if's expecting an constant pattern.

from language.

mraleph avatar mraleph commented on September 23, 2024 1

@lukehutch int() is an object pattern: it matches when newValue is subtype of int (e.g. it is an equivalent of newValue is int).

This works for the setter, but unfortunately not when getting values from SharedPreferences in the constructor. Do you have suggestions for how to handle this?

You could pattern match on the type of this, rather than it's runtimeType:

switch (this) {
  case PersistentValueNotifier<int?>(): 
    value = _sharedPreferences!.getInt(sharedPreferencesKey) as T? ?? initialValue;
  case PersistentValueNotifier<String?>(): 
    // ...
}

There might be few other ways to refactor this (e.g. you can make constructor private and make static methods producing specific types of notifiers: one for integers, one for strings, etc), but this can be too much engineering for too little gain.

I think the shared preferences API itself should have been strongly typed, something like:

sealed class SharedPreferencesKey<T> {
  final String key;
  T getFrom(SharedPreferences p);
  void setOn(SharedPreferences p, T? value); 
}

class StringKey extends SharedPreferencesKey<String> {
  // ...
}

Then this type of code you have would be easier to write. But I digress.

from language.

lukehutch avatar lukehutch commented on September 23, 2024

@mateusfccp @leafpetersen Thanks for explaining, I didn't know any of that!

I expected case int? to work, since this works:

switch (type) {
  case List<int?>:
}

I'll close this, thanks for the information!

from language.

munificent avatar munificent commented on September 23, 2024
switch (type) {
  case List<int?>:
}

That doesn't work either. For patterns, we only allow a pretty small subset of the expression grammar for constants. It's basically just identifiers. List<int?> is a valid type literal expression, but it's not a valid constant pattern. This is entirely a syntactic restriction (because we need to avoid ambiguity between patterns and expressions inside patterns).

This does work:

switch (type) {
  case const (List<int?>):
}

But this doesn't:

switch (type) {
  case const (int?):
}

Because int? isn't a valid type literal expression. Type literals support most kinds of type annotations, but not all of them. (The other big missing one is function types. There's no type literal syntax for a specific function type.)

As @leafpetersen says, a typedef will help.

from language.

lukehutch avatar lukehutch commented on September 23, 2024
switch (type) {
  case List<int?>:
}

That doesn't work either. For patterns, we only allow a pretty small subset of the expression grammar for constants. It's basically just identifiers. List<int?> is a valid type literal expression, but it's not a valid constant pattern. This is entirely a syntactic restriction (because we need to avoid ambiguity between patterns and expressions inside patterns).

@munificent I have the following code working fine in my Dart project:

  @override
  set value(T newValue) {
    // Update the `ValueNotifier` superclass with the new value
    super.value = newValue;
    // Asynchronously write the new value through to SharedPreferences
    switch (runtimeType) {
      case PersistentValueNotifier<int>:
      case PersistentValueNotifier<int?>:
        if (newValue == null) {
          unawaited(_sharedPreferences!.remove(sharedPreferencesKey));
        } else {
          unawaited(_sharedPreferences!
              .setInt(sharedPreferencesKey, newValue as int));
        }
        break;
      case PersistentValueNotifier<bool>:
      case PersistentValueNotifier<bool?>:
        if (newValue == null) {
          unawaited(_sharedPreferences!.remove(sharedPreferencesKey));
        } else {
          unawaited(_sharedPreferences!
              .setBool(sharedPreferencesKey, newValue as bool));
        }
        break;
      case PersistentValueNotifier<double>:
      case PersistentValueNotifier<double?>:
        if (newValue == null) {
          unawaited(_sharedPreferences!.remove(sharedPreferencesKey));
        } else {
          unawaited(_sharedPreferences!
              .setDouble(sharedPreferencesKey, newValue as double));
        }
        break;
      case PersistentValueNotifier<String>:
      case PersistentValueNotifier<String?>:
        if (newValue == null) {
          unawaited(_sharedPreferences!.remove(sharedPreferencesKey));
        } else {
          unawaited(_sharedPreferences!
              .setString(sharedPreferencesKey, newValue as String));
        }
        break;
      case PersistentValueNotifier<List<String>>:
      case PersistentValueNotifier<List<String>?>:
        if (newValue == null) {
          unawaited(_sharedPreferences!.remove(sharedPreferencesKey));
        } else {
          unawaited(_sharedPreferences!
              .setStringList(sharedPreferencesKey, newValue as List<String>));
        }
        break;
      default:
        throw Exception('Type parameter not supported: $runtimeType');
    }
  }

from language.

lukehutch avatar lukehutch commented on September 23, 2024

@mraleph thanks -- what exactly does this int() syntax do?

This works for the setter, but unfortunately not when getting values from SharedPreferences in the constructor. Do you have suggestions for how to handle this?

https://github.com/lukehutch/flutter_persistent_value_notifier/blob/main/lib/src/persistent_value_notifier.dart

from language.

lukehutch avatar lukehutch commented on September 23, 2024

Thank you everyone for your valuable suggestions. I was able to improve the code quality substantially. These are some awesome new features for Dart!

from language.

lukehutch avatar lukehutch commented on September 23, 2024

PS one final question, @mraleph what is wrong with using runtimeType? Is that not available in production builds, or something?

from language.

munificent avatar munificent commented on September 23, 2024

@munificent I have the following code working fine in my Dart project:

Oh, wait. Have you not upgraded to Dart 3.0 yet? That code might work before Dart 3.0, but won't when you upgrade. The object patterns @mraleph suggested are Dart 3.0 features.

PS one final question, @mraleph what is wrong with using runtimeType? Is that not available in production builds, or something?

It's available but runtimeType is slow and breaks Liskov substitution. Consider:

int iterableOrMapLength(Object iterableOrMap) {
  ...
}

This function should return the number of elements or entries in the object if you give it an Iterable or Map. For example:

main() {
  print(iterableOrMapLength([1, 2, 3]);
}

This should print "3". If you implement that function using runtimeType:

int iterableOrMapLength(Object iterableOrMap) {
  if (iterableOrMap.runtimeType == Iterable) {
    return (iterableOrMap as Iterable).length;
  } else if (iterableOrMap.runtimeType == Map) {
    return (iterableOrMap as Map).length;  
  } else {
    throw ArgumentError('Must be Iterable or Map.');
  }
}

Then the example call from main() does not work. The object you're passing it has a runtime type of List, not Iterable. Those are different types, so comparing runtime types using == will return false.

What you really want to ask is "Is this object an instance of Iterable or any subtype of Iterable?" To answer that, you just want a normal is test:

int iterableOrMapLength(Object iterableOrMap) {
  if (iterableOrMap is Iterable) {
    return iterableOrMap.length;
  } else if (iterableOrMap is Map) {
    return iterableOrMap.length;  
  } else {
    throw ArgumentError('Must be Iterable or Map.');
  }
}

Object and variable patterns in switches do is tests too, so this also works:

int iterableOrMapLength(Object iterableOrMap) {
  switch (iterableOrMap) {
    case Iterable():
      return iterableOrMap.length;
    case Map():
      return iterableOrMap.length;  
    default:
      throw ArgumentError('Must be Iterable or Map.');
  }
}

Or:

int iterableOrMapLength(Object iterableOrMap) {
  switch (iterableOrMap) {
    case Iterable iterable:
      return iterable.length;
    case Map map:
      return map.length;  
    default:
      throw ArgumentError('Must be Iterable or Map.');
  }
}

Or if you really want to go whole hog and use all the new 3.0 stuff:

int iterableOrMapLength(Object iterableOrMap) => switch (iterableOrMap) {
  Iterable(:var length) || Map(:var length) => length,
  _ => throw ArgumentError('Must be Iterable or Map.')
};

In general, runtimeType is almost never what you want. 99% of the time, you want is tests on instances.

from language.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.