what I did:
extension BigDate on DateTime {
String get locatedWeekDay {
switch (weekday) {
case DateTime.sunday:
return "Sun";
case DateTime.monday:
return "Mon";
case DateTime.tuesday:
return "Tue";
......
default:
throw Exception();
}
}
}
class JapanDate extends DateTime {
#override
String get locatedWeekDay {
switch (weekday) {
case DateTime.sunday:
return "日";
case DateTime.monday:
return "月";
......
default:
throw Exception();
}
}
}
now I just run this:
DateTime d = JapanDate(2022, 3, 2);
print(d.locatedWeekDay);
it returns me "Wed" oh, can you help me to fix it?
I tried: to add #override to the get method, add the import to the first line.
Extension methods are static; they are compile-time syntactic sugar for an equivalent freestanding function. As such, they are wholly dependent on the static type of the variable (whether explicitly declared or inferred), which in your case is DateTime. They cannot be overridden since overrides involve runtime polymorphism.
What you could do instead is:
Create a base class (or mixin) that provides the locatedWeekDay interface.
Make JapanDate derive from that interface.
Make your extension method check if this implements that interface, falling back to a default implementation if it's an ordinary DateTime object.
abstract class HasLocatedWeekDay {
String get locatedWeekDay;
}
extension BigDate on DateTime {
String get locatedWeekDay {
// Type-promotion for `this` is not yet supported.
// See: <https://github.com/dart-lang/language/issues/1397>
final self = this;
if (self is HasLocatedWeekDay) {
return self.locatedWeekDay;
}
switch (weekday) {
case DateTime.sunday:
return "Sun";
case DateTime.monday:
return "Mon";
case DateTime.tuesday:
return "Tue";
......
default:
throw Exception();
}
}
}
class JapanDate extends DateTime implements HasLocatedWeekDay {
#override
String get locatedWeekDay {
switch (weekday) {
case DateTime.sunday:
return "日";
case DateTime.monday:
return "月";
......
default:
throw Exception();
}
}
}
Just don't cast it to DateTime leave it as JapanDate and the code above should work as you expect it to.
JapanDate d = JapanDate(2022, 3, 2);
print(d.locatedWeekDay);
There is no locatedWeekDay method on DateTime. Therefore, it can't be overridden, but you can create a custom class extending this or use an extension as you did for BigDate.
To create a custom class, you need to pass data to super class.
class JapanDate extends DateTime {
JapanDate(int year,
[int month = 1,
int day = 1,
int hour = 0,
int minute = 0,
int second = 0,
int millisecond = 0,
int microsecond = 0])
: super(
year,
month = month,
day = day,
hour = hour,
minute = minute,
second = second,
millisecond = millisecond,
microsecond = microsecond);
String get locatedWeekDay {
switch (this.weekday) {
case DateTime.sunday:
return "日";
case DateTime.monday:
return "月";
//....
default:
return "N";
}
}
}
Now you can use JapanDate class with locatedWeekDay.
JapanDate d = JapanDate(2022, 3, 7);
print(d.locatedWeekDay); ///月
About .weekday on DateTime. It is defined as
external int get weekday;
External Functions
An external function is a function whose body is provided separately from its
declaration. An external function may be a top-level function (17), a method
You can follow this What does external mean in Dart?
Related
I want to achieve fast enumeration so that I can express this enum as a contiguous monotonic sequence of unique integer values, one for each case (starting at zero). I want the order of assignments to be in the same order as they appear in the declaration.
In this particular example, the sequence would simply be 0, 1 and 2. Is there an expression I can use to achieve this? Or is it only possible by manually typing out this by hand?
I want to have two hashable implementations, so I can establish all possible varieties or simply how many cases I have.
enum Score: {
case snap(Rank) // 6 varieties
case double(Power) // 4 varieties
case highFive
var value: Int {
// ...
}
}
enum Rank: Int {
//..
}
enum Power: Int {
// ..
}
It seems this is not possible.
I either choose to maintain two enums that are identical except one has associated types and the other does not.
Or I manually implement the solution.
Manual
So for a poker game, I would establish an order of importance for each hand, as per the rules of Texas Hold'em.
private extension Hand {
var precedence: Int {
switch self {
case .highCard:
return 0
case .pair:
return 1
case .twoPair:
return 2
case .threeKind:
return 3
case .straight:
return 4
case .fourKind:
return 5
case .flush:
return 6
case .fullHouse:
return 7
case .straightFlush:
return 8
case .royalFlush:
return 9
}
}
}
When the above is in place, I can then produce a toggle.
extension Hand: CaseIterable {
public static var allCases: [Self] {
let ranks = Rank.allCases
return ranks.map(highCard) +
ranks.map(pair) +
ranks.map(twoPair) +
ranks.map(threeKind) +
ranks.map(straight) +
ranks.map(fourKind) +
ranks.map(flush) +
ranks.map(fullHouse) +
ranks.map(straightFlush) +
[royalFlush]
}
/// Establish every unique case.
/// - Parameter ignore: If true, do not unique by rank.
/// - Returns: Every possible case, with or without rank as a uniquing restriction.
public static func allCases(ignoringRank: Bool) -> [Self] {
switch ignoringRank {
case true:
var tally = Set<Int>()
return Self.allCases.filter {
switch tally.contains($0.precedence) {
case true:
return false
case false:
tally.insert($0.precedence)
return true
}
}
case false:
return Self.allCases
}
}
}
The following is now possible.
private extension Hand {
static var assessments: [Assessable.Type] {
allCases(ignoringRank: true).map {
$0.assessment
}
}
private var assessment: Assessable.Type {
switch self {
case .highCard:
return HighCardAssessor.self
case .pair:
return PairAssessor.self
case .twoPair:
return TwoPairAssessor.self
case .threeKind:
return ThreeKindAssessor.self
case .straight:
return StraightAssessor.self
case .fourKind:
return FourKindAssessor.self
case .flush:
return FlushAssessor.self
case .fullHouse:
return FullHouseAssessor.self
case .straightFlush:
return StraightFlushAssessor.self
case .royalFlush:
return RoyalFlushAssessor.self
}
}
}
An assessor is a type that implements evaluate(cards:) to determine if a hand exists. So being able to loop like this is useful.
I have some hardcoded string, based on that i have created switch case. But instead of string in switch case i am trying to create Enum for same. but i am not sure how to do that. I dont want to use default case.
Do i need to access with raw value or any other better way to do ?
enum screens: String {
case faq = "faq", contactus = "contactus", termncondi = "termncondi", dashoboard = "dashoboard"
}
func deepLink(text: String) -> String {
switch text {
case "faq":
return (FAQ.localized())
case "contactus":
return (Contactus.localized())
case "termncondi":
return (Term.localized())
case "dashoboard":
return (Dashboard.localized())
default:
return ""
}
}
You can simply define the enum and override the rawValue property like so:
enum Link {
case faq
case contact
...
var rawValue: String {
switch self {
case .faq:
return FAQ.localized()
case .contact:
return Contact.localized()
default:
return "Unknown case"
}
}
}
And get its rawValue.
let faqLink = Link.faq.rawValue
You can also perform a switch on an enum instance just like you do with a string.
In order to get hardcoded values do something like:
public extension String {
static func getScreens(name: DeepLink) -> String {
return name.rawValue
}
public enum DeepLink: String {
case faq = "faq"
case contactus = "contactus"
// and so on
}
}
I have a Dart enum which looks like this:
enum Gender {
#JsonValue(0)
male,
#JsonValue(1)
female,
}
I have created a dart extension that returns the String name and int value. It looks something like this -
extension GenderExtention on Gender {
String get name {
switch (this) {
default:
return _getDefaultName(this);
}
}
//For the enum male, it returns "Male"
String _getDefaultName(Community value) {
if (value == null) {
return null;
}
String valueStr = value.toString();
String enumName = valueStr.substring(valueStr.indexOf('.') + 1);
return enumName[0].toUpperCase() + enumName.substring(1);
}
int get value {
switch (this) {
case Gender.male:
return 0;
case Gender.female:
return 1;
default:
return null;
}
}
}
This becomes painful for larger enums, especially the value section.
Are there any suggestions on how to get the enum value (0 for #JsonValue(0)) more easily than manually defining it in the extension? Would using something like reflections help here?
the only way to access annotations at run-time is indeed reflection using dart:mirrors. That library is not available when compiling to the web or for Flutter, so it's probably not going to solve your problem.
What you can do here is:
int get value => this.index;
That only works if the values are actually the same as the index (0 for the first declared value, 1 for the next, etc.)
I saw this piece of code today, and was wondering why you would not instead use simple static stored properties?
This is the code that I am curious about:
class ApiKeys {
// movie keys
class var HomePage: String { get { return "homepage" } }
class var Id: String { get { return "id" } }
class var Overview: String { get { return "overview" } }
class var PosterPath: String { get { return "poster_path" } }
class var ReleaseDate: String { get { return "release_date" } }
class var Runtime: String { get { return "runtime" } }
class var Tagline: String { get { return "tagline" } }
class var Title: String { get { return "title" } }
class var Rating: String { get { return "vote_average" } }
// query params
class var ApiKey: String { get { return "api_key" } }
class var Query: String { get { return "query" } }
}
And this is how I would have written the same code:
class ApiKeys {
static let homePage = "homepage"
static let id = "id"
static let overview = "overview"
static let posterPath = "poster_path"
static let releaseDate = "release_date"
static let runtime = "runtime"
static let tagline = "tagline"
static let title = "title"
static let rating = "vote_average"
//Query Params
static let ApiKey = "api_key"
static let query = "query"
}
There won't ever be any need to override the variables, so use of static should be okay. Am I missing something? Is there any advantage or reason to use the first method over the second?
For what it's worth, I wouldn't be inclined to use computed or stored properties at all. Rather than defining this to be a class, this seems like a textbook case for an enum:
enum ApiKey: String {
// movie keys
case HomePage = "homepage"
case Id = "id"
case Overview = "overview"
case PosterPath = "poster_path"
case ReleaseDate = "release_date"
case Runtime = "runtime"
case Tagline = "tagline"
case Title = "title"
case Rating = "vote_average"
// query params
case ApiKey = "api_key"
case Query = "query"
}
This more accurately captures the notion that a "key" can be one of those values.
And you'd use it like so:
if key == ApiKey.HomePage.rawValue {
...
}
Or
if ApiKey(rawValue: key) == .HomePage {
...
}
In answer to your original question, “when should I prefer computed properties”, the answer is that you generally use them to retrieve a value computed from other properties and, optionally, if you want to set other (possibly private) properties and values indirectly. There's little benefit to using computed properties if you're just going to return some static, unchanging string.
A class var can be overridden by a subclass while a static constant can't. That's the first difference I can think about.
Computed properties can be used to dynamically change the value of the property at runtime if necessary, just like and overridden getter can in Objective-C. You can't do that with a static let constant.
Possibly somewhat off-topic: but one possibly contrived usage scenario where static stored properties cannot be used is if you define non-blueprinted static computed properties with default implementations in an extension to some "constants" protocol. Classes/structs/etc that conform to such a protocol can be allowed to access type constrained generics, where these generics are the the only context in which the protocol constants are accessible (limit the accessibility to the constants) where they are guaranteed to be constants (since they can also be used directly from the concrete types that conform that protocol, but these can "override" the "constants" with new values).
protocol HasAccessToConstants {
/* since we don't blueprint 'theAnswer', the default
implementation below will always be used for objects
conforming to this protocol when used in a generic
context (even if they attempt to "override" these
"constants" with implementations of their own, these
custom ones can only be accessed for concrete-types). */
}
extension HasAccessToConstants {
static var theAnswer: Int { return 42 }
/* for protocols: we may implement a default
implementation only for computed properties */
}
class Foo : HasAccessToConstants {
/* Even if the developer implements its own "constant"
implementation, this will not be used for accessing
Foo type in a generic context. */
static var theAnswer: Int { return 9 }
}
func onlyForObjectsWithAccessToConstants<T: HasAccessToConstants>(obj: T) {
// do something with obj ...
// make use of constants available to the type of obj
print("Constants available to the type of this object (e.g. '\(T.theAnswer)')")
}
onlyForObjectsWithAccessToConstants(Foo())
/* Constants available to the type of this object (e.g. '42') */
// not really "constants" as they can be "overridden" for concrete types
print(Foo.theAnswer) // 9 (since concrete type)
Again, contrived, and included for the technical discussion, as I can't really see in what scenario this would be more useful than other, better alternatives.
I want to get the parameter types of a Haxe function using a macro and convert them to a shorthand string form, a bit like JNI/Java method signatures, but without a return type.
The motivation here is to provide access to the function parameter types, without having to slowly search through run-time type information at runtime. For example, say you want to construct a graphical widget for calling a function that takes parameters. You will need the type of each function parameter to create the correct spinbox, textbox, and select box widgets needed for tweaking the values that will be passed to the function.
So the question is, how can you save Haxe function parameter types with a macro?
Here is a macro that works for a few basic types, and any abstracts based on those types. It maps the function parameter types to strings. For example, function type String->Float->Int->String->Void maps to sfis, Float->Float->Int to ff etc:
package;
import haxe.macro.Expr;
import haxe.macro.Context;
import haxe.macro.Type;
import haxe.macro.ExprTools;
// Map some Haxe types to string ids
#:enum abstract TypeMapping(String) from (String) {
var BOOL = "b";
var FLOAT = "f";
var INT = "i";
var STRING = "s";
}
class Util
{
public macro static function getParameterTypes(f:Expr):ExprOf<String> {
var type:Type = Context.typeof(f);
if (!Reflect.hasField(type, 'args')) {
throw "Parameter has no field 'args'";
}
var t = type.getParameters()[0];
var args:Array<Dynamic> = Reflect.field(type, 'args')[0];
var signature:String = "";
for (i in 0...args.length) {
switch(args[i].t) {
case TAbstract(t, p):
var underlyingTypeName = Std.string(t.get().type.getParameters()[0]);
switch(underlyingTypeName) {
case "Bool":
signature += TypeMapping.BOOL;
case "Float":
signature += TypeMapping.FLOAT;
case "Int":
signature += TypeMapping.INT;
case "String":
signature += TypeMapping.STRING;
default:
throw "Unhandled abstract function parameter type: " + underlyingTypeName;
}
case CString:
signature += TypeMapping.STRING;
default:
throw "Unhandled function parameter type: " + args[i];
}
}
return macro $v{signature};
}
}
A further problem is how to make this work for all types, rather than just ones you handle explicitly. To do that, you might populate an array of Strings with the type name/class name/path of each function parameter instead, and return that instead of a single String. Here's an attempt at that, note it doesn't work with function parameters (and probably other stuff) yet:
public macro static function getFullParameterTypes(f:Expr):ExprOf<Array<String>> {
var type:Type = Context.typeof(f);
if (!Reflect.hasField(type, 'args')) {
throw "Parameter has no field 'args'";
}
var args:Array<Dynamic> = Reflect.field(type, 'args')[0];
var pos = haxe.macro.Context.currentPos();
var signature:Array<Expr> = [];
for (i in 0...args.length) {
var argType:Type = args[i].t;
var s;
switch(argType) {
case TFun(t, r):
s = EConst(CString("Function"));
throw "Not working with function parameters yet";
case _:
s = EConst(CString(argType.getParameters()[0].toString()));
}
signature.push({expr: s, pos: pos});
}
return macro $a{signature};
}
A more up to date approach..
macro function deflate(fun:haxe.macro.Expr) {
var type = haxe.macro.Context.typeof(fun);
final paramNames = extractFunction(type);
return macro $v{paramNames};
}
// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
return switch type {
case TFun(args, ret): {
var paramNames:Array<Dynamic> = [];
for (p in args) {
final pName = p.name;
paramNames.push(pName);
}
return paramNames;
}
case _: {throw "unable to extract function information";};
}
}
Use it like this
using Macros;
function func(name:String, greeting:String){};
final args = fun.deflate();
trace(args) // output: [name, greeting]
A problem you may face is how to collect the default value of a parameter, consider the example below.
function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = fun.deflate();
trace(args) // output: [name, greeting]
Now let's account for default parameter values by slightly modifying the code:
// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
return switch type {
case TFun(args, ret): {
var paramNames:Array<Dynamic> = [];
for (p in args) {
final pName = p.name;
final v = {name: pName, value: null}; // <= anticipate a value
paramNames.push(v);
}
return paramNames;
}
case _: {throw "unable to extract function information";};
}
}
macro function deflate(fun:haxe.macro.Expr) {
var type = haxe.macro.Context.typeof(fun);
final paramNames:Array<Dynamic> = extractFunction(type);
// extract default param values
switch fun.expr {
case EFunction(f, m):{
for(a in m.args){
for(p in paramNames){
if(p.name == a.name){
if(a.value != null){
switch (a.value.expr){
case EConst(c):{
switch(c){
case CString(v, _):{
p.value = v;
}
case CFloat(f): {
p.value = Std.parseFloat(f);
}
case CInt(i):{
p.value = Std.parseInt(i);
}
case _: throw "unsupported constant value for default parameter";
}
}
case _:
}
}
}
}
}
}
case _:
}
return macro $v{paramNames};
}
So we can now use it like this
function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = Macros.deflate(func);
trace(args) // output: [{name: 'name', value:'Josh', {name:'greeting', value:'Hello'}]