How can I overload the + operator on String in dart? - dart

I tried to overload the + operator on the String class to be able to add an int to a String. 'A'+1 should produce 'B'. But it does not work in dartpad. How can I make it work?
extension StringExt on String {
String operator +(int i) {
return String.fromCharCode(this.codeUnitAt(0) + i);
}
}
void main() {
String s = 'A';
print('A' + 1);
}

You cannot add an extension method named + on String since String already has an operator named +.
Dart can only have one member with each name on each class (no member overloading), and extension members have lower priority than existing instance members.
You can actually call the extension +, but you need to explicitly invoke the extension to avoid String.operator+ taking precedence:
print(StringExt('A') + 1);
That removes most of the advantage of having a short operator.
As mentioned, you can use a different name:
extension CharCodeInc on String {
String operator /(int i) => String.fromCharCode(codeUnitAt(0) + i);
}
...
print("A"/2); // prints "C"
Too bad all the good names are taken (at least + and * on strings).

Related

How to convert int to double and vice-versa when parsing using num.parse?

Minimal reproducible code:
void main() {
print(foo<int>('1'));
print(foo<double>('1.0'));
print(foo<double>('1'));
print(foo<int>('1.0'));
}
T foo<T extends num>(String s) {
final res = num.parse(s);
return (T == double ? res.toDouble() : res.toInt()) as T;
}
As you can see I'm manually handling the types. Is there any better way to do it?
I don't see a much better solution. You have a function which changes behavior entirely based on the type argument. Since all you can do with a type argument is subtype-checks, or comparing it to a constant type literal, you need to do something like that.
I'd prefer subtype-checks, because that also allows promotion, but for something limited like this, where there are only four possible types for T, checking Type object equality can also work. There'll just need to be at least one as T before returning.
Either approach also only works for type hierarchies which are finite, and you account for all the possible types. Even here, the current code is not covering <num> and <Never> which are also valid type arguments for the bound num. So, be vigilant.
Maybe, using subtype checks for promotion:
T parse<T extends num>(String source) {
var value = num.parse(source);
if (value is T) return value;
// T is not `num`.
num d = value.toDouble();
if (d is T) return d; // T was double.
try {
num n = value.toInt(); // Can fail for Infinity/NaN
if (n is T) return n; // T was int
} catch (_) {
// T was `int` after all, so throwing was correct.
if (1 is T) rethrow; // T was int, but the input was not valid.
}
// T was neither num, double, nor int, so must be `Never`.
throw ArgumentError.value(T, "T", "Must not be Never");
}
Or. using Type object equality:
T parse<T extends num>(String source) {
var value = num.parse(source);
switch (T) {
case num: return value as T;
case int: return value.toInt() as T;
case double: return value.toDouble() as T;
default:
throw ArgumentError.value(T, "T", "Must not be Never");
}
}

+ isn't defined for the type 'Object

I keep getting this error => "The operator '+' isn't defined for the type 'Object'. (view docs). Try defining the operator '+'."
How could I define it?
import 'dart:math';
bool isArmstrongNumber(int number) {
var numberString = number.toString();
return number ==
numberString.split("").fold(0,
(prev, curr) => prev! + pow(int.parse(curr), numberString.length));
}
main() {
var result = isArmstrongNumber(153);
print(result);
}
fold in Dart can have some problems when it comes to automatically determine what type it should return and handle. In these cases, we need to manually enter the type like this (fold<int>()):
import 'dart:math';
bool isArmstrongNumber(int number) {
final numberString = number.toString();
return number ==
numberString.split("").fold<int>(
0,
(prev, curr) =>
prev + pow(int.parse(curr), numberString.length).toInt(),
);
}
void main() {
final result = isArmstrongNumber(153);
print(result); // true
}
I also fixed a problem where pow returns num which is a problem. In this case, we can safely just cast it to int without issues.
Details about this problem with fold
The problem here is that Dart tries to guess the generic of the fold based on the expected returned type of the method. Since the == operator expects an Object to compare against, fold will also expect to just return Object and the generic ends up being fold<Object>.
This is not a problem for the first parameter since int is an Object. But it becomes a problem with your second argument where you expect an int object and not Object since Object does not have the + operator.

How to return two specific types from a generic method?

T getValue<T>(int i) {
if (T == String) return '$i'; // Error
return i; // Error
}
void main() {
var s = getValue<String>(1);
var i = getValue<int>(1);
}
I want getValue to return string if T is String and int otherwise. How to do that?
You can't restrict the type parameter to just int or String, so it will have to accept more than that (at least their least common supertype, Object, so basically any type).
It's not a particularly helpful way to code. It's possible, but not recommended:
T getValue<T>(int i) {
if (i is T) return i;
return "$i" as T;
}
This will return the int if T allows it (so T being any of int, or a super type of int, which is num, Object, dynamic or void, or any number of Comparable<X> wrappings around any any of those supertypes), and otherwise try to return a string. That will fail with a type error unless T is String (since we've already ruled out all supertypes of String).
You can still call it as getValue<bool>(42) and watch it fail, so the type argument doesn't help with correctness.
It's not particularly effective. I'd rather do:
dynamic getValue(int i, {bool toString = false}) {
if (toString) return "$i";
return i;
}
and call it as:
String x = getValue(42, toString: true); // Add `as String` if you disable downcasts.
int y = getValue(42); // And `as int` here.
The type parameter is really just making things harder. You are going to cast or type-check the result anyway, so might as well do it at the call point, rather than introduce type variables that aren't actually preventing misuse anyway.
(I'd probably just do two different functions, but I assume that there is a reason for wanting one function).
As I mentioned in the comments, I don't see any way that you could use your generic as the return type of your getValue function. Even assuming the return under the if statement worked, there is nothing that can be done about trying to return int i when List is passed as the type. You'll be trying to return an int as a List.
If you change it to dynamic, your code will work fine as it's just using the generic as another parameter.
dynamic getValue<T>(int i) {
if (T == String) return '$i';
return i;
}
void main() {
var s = getValue<String>(1);
var i = getValue<int>(1);
}

Swift: Precedence for custom operator in relation to dot (".") literal

In Swift 3, I have written a custom operator prefix operator § which I use in a method taking a String as value returning a LocalizedString struct (holding key and value).
public prefix func §(key: String) -> LocalizedString {
return LocalizedString(key: key)
}
public struct LocalizedString {
public var key: String
public var value: String
public init(key: String) {
let translated = translate(using: key) // assume we have this
self.key = key
self.value = translated ?? "!!\(key)!!"
}
}
(Yes I know about the awesome L10n enum in SwiftGen, but we are downloading our strings from our backend, and this question is more about how to work with custom operators)
But what if we wanna get the translated value from the result of the § operator (i.e. the property value from the resulting LocalizedString)
let translation = §"MyKey".value // Compile error "Value of type 'String' has no member 'value'"
We can of course easily fix this compile error by wraping it in parenthesis (§"MyKey").value. But if do not want to do that. Is it possible to set precedence for custom operators in relationship to the 'dot' literal?
Yes I know that only infix operators may declare precedence, but it would make sense to somehow work with precedence in order to achieve what I want:
precedencegroup Localization { higherThan: DotPrecedence } // There is no such group as "Dot"
prefix operator §: Localization
To mark that the Swift compiler first should evaluate §"MyKey" and understand that is not a string, but in fact an LocalizedString (struct).
Feels unlikely that this would be impossible? What am I missing?
The . is not an operator like all the other ones defined in the standard library, it is provided by the compiler instead. The grammar for it are Explicit Member Expressions.
Having a higher precedence than the . is nothing the compiler should enable you to do, as it's such a fundamental use case. Imagine what you could do if the compiler enabled such a thing:
-"Test".characters.count
If you could have a higher precedence than ., the compiler has to check all possibilities:
(-"Test").characters.count // func -(s: String) -> String
(-("Test".characters)).count // func -(s: String.CharacterView) -> String.CharacterView
-("Test".characters.count) // func -(s: Int) -> Int
Which would
Potentially increase the compile time a lot
Be ambiguous
Possibly change behaviour of existing code upon adding overloads
What I suggest you to do is abandon the idea with a new operator, it's only going to be adding more cognitive load by squashing some specific behaviour into a single obscure character. This is how I'd do it:
extension String {
var translatedString : String {
return translate(using: self)
}
}
"MyKey".localizedString
Or if you want to use your LocalizedString:
extension String {
var localized : LocalizedString {
return LocalizedString(key: self)
}
}
"MyKey".localized.value
These versions are much more comprehensive.

What do _:_: and similar combinations of the colon and underscore mean in Swift? [duplicate]

This question already has answers here:
What is _: in Swift telling me?
(3 answers)
Closed 7 years ago.
When reading Swift's documentation, Apple commonly uses functionName(_:name:) or something similar. What exactly is this pattern that sometimes is _:_:, sometimes just _:, and _:name:. I think it has to do with parameter shorthand, but I'm not sure, and can't find the explanation in Swift's programming guide. Thank you!
Example:
insert(_:atIndex:)
The underscore indicates that there is no external parameter name for the function. Apple's Swift Documentation talks about this concept in terms of when you're writing your own functions.
Take the case where you write this function (from the documentation):
func sayHello(to person: String, and anotherPerson: String) -> String { ... }
If you were to use the function you would write the following:
sayHello(to: "Bill", and: "Ted")
The signature for this would be sayHello(to:and:).
However, what if we wanted to use the function as sayHello("Bill", "Ted")? How would we indicate that we didn't want an external parameter name? Well that's where the underscore comes in. We could rewrite the function as:
func sayHello(person: String, _ anotherPerson: String) -> String { ... }
Note the first parameter doesn't need the _, but subsequent ones will. The first is inferred to have no parameter name. This makes the method signature for this call sayHello(_:_:) because you as the caller don't have a named parameter.
Update Swift 3.0:
Swift 3.0 treats all parameters equally. The first parameter now requires an underscore to indicate the absense of an external parameter name. In the above example of having sayHello("Bill", "Ted") at the call site, your corresponding function or method declaration would have to be
func sayHello(_ person: String, _ anotherPerson: String) -> String { ... }
Note the addition of the underscore before the internal parameter name 'person'.
The underscore indicates an ignored value. You can read more about it here:
What's the _ underscore representative of in Swift References?
In the documentation it is being used as a wildcard to indicate a function that takes an unnamed parameter.
share my learning experience.
//MARK: Default
func join1(s1: String, s2: String, joiner: String) -> String {
println("join1" + s1 + joiner + s2)
return s1 + joiner + s2
}
//MARK: External Parameter Names
// To make the purpose of these String values clearer, define external parameter names for each join function parameter
func join2(string s1: String, toString s2: String, withJoiner joiner: String) -> String {
println("join2" + s1 + joiner + s2)
return s1 + joiner + s2
}
//MARK: External Names for Parameters with Default Values
func join3(string s1: String, toString s2: String, withJoiner joiner: String = " ") -> String {
println("join3" + s1 + joiner + s2)
return s1 + joiner + s2
}
//MARK: No External Names for Parameters with Default Values
func join4(s1: String, s2: String, joiner: String = " ") -> String {
println("join4" + s1 + joiner + s2)
return s1 + joiner + s2
}
//MARK: instead of an explicit external name
// You can opt out of this behavior by writing an underscore (_) instead of an explicit external name when you define the parameter. However, external names for parameters with default values are preferred.
func join5(s1: String, _ s2: String, _ joiner: String = " ") -> String {
println("join5" + s1 + joiner + s2)
return s1 + joiner + s2
}
//MARK: Shorthand External Parameter Names
// Provide an external parameter name with the same local parameter name
// Swift provides an automatic external name for any parameter that has a default value. The automatic external name is the same as the local name, as if you had written a hash symbol before the local name in your code.
func join6(#s1: String, s2: String, joiner: String = " ") -> String {
println("join6" + s1 + joiner + s2)
return s1 + joiner + s2
}
And you can see how to use them
join1("s1", s2: "s2", joiner: "-")
join2(string: "s1", toString: "s2", withJoiner: "-")
join3(string: "s1", toString: "s2", withJoiner: "-")
join3(string: "s1", toString: "s2")
join4("s1", s2: "s2", joiner: "-")
join4("s1", s2: "s2")
join5("s1", "s2", "-")
join5("s1", "s2")
join6(s1: "s1", s2: "s2", joiner: "-")
join6(s1: "s1", s2: "s2")
From the Swift docs:
Function parameters can have both a local name (for use within the
function’s body) and an external name (for use when calling the
function), as described in External Parameter Names. The same is true
for method parameters, because methods are just functions that are
associated with a type. However, the default behavior of local names
and external names is different for functions and methods.
...
...
Specifically, Swift [makes] the first parameter name in a method a local
parameter name by default, and [makes] the second and subsequent
parameter names both local and external parameter names by default.
This convention matches the typical naming and calling convention you
will be familiar with from writing Objective-C methods, and makes for
expressive method calls without the need to qualify your parameter
names.
Consider this alternative version of the Counter class...
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes: Int) {
count += amount * numberOfTimes
}
}
This incrementBy(_:numberOfTimes:) method has two parameters—amount
and numberOfTimes. By default, Swift treats amount as a local name
only, but treats numberOfTimes as both a local and an external name.
You call the method as follows:
let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3)
// counter value is now 15
You don’t need to define an external parameter name for the first
argument value, because its purpose is clear from the function name
incrementBy. The second argument, however, is qualified by an external
parameter name to make its purpose clear when the method is called.
This default behavior effectively treats the method as if you had
written a hash symbol (#) before the numberOfTimes parameter:
func incrementBy(amount: Int, #numberOfTimes: Int) {
count += amount * numberOfTimes
}
The default behavior described above means that method definitions in
Swift are written with the same grammatical style as Objective-C, and
are called in a natural, expressive way.
So, to nullify the external name of the second parameter of a method you can explicitly write '_' for the external name:
class Counter {
var count: Int = 0
func incrementBy(amount: Int, _ numberOfTimes: Int) {
count += amount * numberOfTimes
}
}
Now the syntax for the method name becomes:
incrementBy(__:__:)
The syntax tells you how to call the method. In this case, the syntax tells you that there are two un-named method arguments, so you call the method like this:
incrementBy(3, 2)
If the method name is incrementBy(_:numberOfTimes:), the syntax tells you that the first argument is un-named while the second argument's name is numberOfTimes, so you call the method like this:
incrementBy(3, numberOfTimes:2)

Resources