-
Notifications
You must be signed in to change notification settings - Fork 205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Interface support for extensions #736
Comments
That's a very different feature than static extension methods. For this to work, the call to What you can do is pass an print(json.encode(data,toEncodable: (dynamic o) {
if (o is Implicit) return o.toJson();
return o.toJson();
}); |
It will be convenient for implementing interfaces in type checking condition expression, whereType, ... . import 'dart:convert';
abstract class Serializable {
Map<String, Object> toMap();
}
class Explicit implements Serializable {
@override
Map<String, Object> toMap() => {'type' : 'Explicit'};
}
class Implicit {
Map<String, Object> toMap() => {'type' : 'Implicit'};
}
extension ImplicitSerializable on Implicit implements Serializable {
@override
Map<String, Object> toMap() => this.toMap();
}
class NoImplicit {}
extension NoImplicitSerializable on NoImplicit implements Serializable {
@override
Map<String, Object> toMap() => ... ;
}
void main() {
var data = <Serializable>[Explicit(), Implicit(), NoImplicit()];
print(json.encode(data, toEncodable: (dynamic o) {
if (o is Serializable) return o.toMap();
return o.toJson();
}));
} |
@tatumizer This is not on-topic for this question, but ... Did consider it, do not intend to make it an actual wrapper object. The idea with static extensions is that there is no wrapper. Even if we allow you to use the extension as a type (#397), which would allow you to write |
Tempted. ;-) We could use something like #631 to add an implementation of each member that isn't implemented otherwise, such that it will invoke the extension methods: template mixin Forward<X> {
final X forwardee;
template R get g => forwardee.g;
template R m<T>(P) => forwardee.m<T>(P);
template set s(P) => forwardee.s = P;
}
class Serializable {...}
class Implicit {...}
extension ImplicitSerializable on Implicit {...}
class WrapImplicitAsSerializable with Forward<Implicit>
implements Implicit, Serializable {
final Implicit forwardee;
implicit WrapImplicitAsSerializable(this.forwardee);
// All unimplemented members are generated according to the templates.
} An instance of [Edit Dec 13] We could use an So even though a static extension does not involve wrapper objects, we could have some other mechanism that allows us to specify wrapper objects. |
That would be convenient indeed. If we have template mixins we may still want to specify that this usage of
Adding it up, we'd have consistent consistency! We can't allow that, of course. ;-) |
Same question was also discussed in #475 |
Swift can do it! protocol TextRepresentable {
var textualDescription: String { get }
}
extension Dice: TextRepresentable {
var textualDescription: String {
return "A \(sides)-sided dice"
}
} https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID277 I'm running into this problem because Dart protos have poor mixin support, so it's very difficult to abstract above the GeneratedMessage interface without resorting to wrappers everywhere. If extensions could implement interfaces, I'd be able to define a common interface and attach the GeneratedMessage specific implementation as an extension while being able to write other kinds of objects that implement the same interface, but aren't actually a GeneratedMessage. |
Just thought I'd chime in with a potential use-case for this: abstract class Freeable {
void free();
}
extension on FreeableMemoryArray on MemoryArray with Freeable {
// MemoryArray already has a 'free' function, it would be nice to allow this to work
}
extension on Pointer with Freeable {
void free() {
allocation.free(this);
}
}
// then I could write something like this:
R autoFree<T extends Freeable, R>(T t, R Function(T t) operation) {
try {
return operation(t);
} finally {
t.free();
}
} (of course, if dart supported some sort of native autoFree/using/etc syntax which I think there's another issue about, this wouldn't be necessary, but there's other use-cases where it would still be useful). |
Aren't those called type classes? :) |
comment at 2021/08 ....😓 Today, i just want to implement the please support
// example
// the `StringArbitraryExt` (extension name) is redundant.
extension StringArbitraryExt on String implements Arbitrary<String> {
//....
}
class Arbitrary<T> {
static T arbitrary();
}
extension StringArbitraryExt on String implements Arbitrary<String> {
static String arbitrary() {
return "TODO: generate random string for testing here !";
}
// usage
void check<A extends Arbitrary<A>>({required String message, required int size= 100, required bool Function(A) prop}) {
for (int i = 0; i < size; i++) {
A value = A.arbitrary();
if (!prop(value)) {
A smallerValue = iterateWhile(
condition: (A randomA) => !prop(randomA),
initialValue: value,
next: (A randomA) => randomA.smaller()
);
print("\"${message}\" doesn't hold: ${smallerValue}");
return;
}
}
print("\"${message}\" passed ${size} tests.");
}
// x is a random String
check(message: "XXX should behave like YYY", prop: (String x) => qSort(x) == stdLib.sort(x) );
BTW, swift can do it. import Foundation
func tabulate<A>(times: Int, f: (Int) -> A) -> [A] {
Array(0..<times).map(f)
}
func iterateWhile<A>(condition: (A) -> Bool, initialValue: A,
next: (A) -> A?) -> A {
if let x = next(initialValue) {
if condition(x) {
return iterateWhile(condition: condition, initialValue: x, next: next)
}
}
return initialValue
}
extension Character {
func toInt() -> Int {
var intFromCharacter: Int = 0
for scalar in String(self).unicodeScalars {
intFromCharacter = Int(scalar.value)
}
return intFromCharacter
}
}
public protocol Smaller {
func smaller() -> Self?
}
public protocol Arbitrary {
static func arbitrary() -> Self
}
extension Int: Arbitrary {
public static func arbitrary() -> Int {
Int.random(in: Int.min...Int.max)
}
}
extension Int: Smaller {
public func smaller() -> Int? {
self == 0 ? nil : self / 2
}
}
extension Character: Arbitrary {
public static func arbitrary() -> Character {
let start: Int = ("A" as Character).toInt()
let end: Int = ("Z" as Character).toInt()
return Character(UnicodeScalar(Int.random(in: start...end)) ?? "A")
}
}
extension Character: Smaller {
public func smaller() -> Character? {
nil
}
}
extension String: Arbitrary {
public static func arbitrary() -> String {
let randomLength = Int.random(in: 0...40)
let randomCharacters = tabulate(times: randomLength) { _ in
Character.arbitrary()
}
return randomCharacters.reduce("") {
$0 + String($1)
}
}
}
extension String: Smaller {
public func smaller() -> String? {
self.isEmpty ? nil : String(self.dropFirst())
}
}
extension Array: Arbitrary where Element: Arbitrary {
public static func arbitrary() -> [Element] {
let randomLength = Int.random(in: 0...50)
return tabulate(times: randomLength) { _ in
Element.arbitrary()
}
}
}
extension Array: Smaller where Element: Arbitrary {
public func smaller() -> [Element]? {
self.isEmpty ? nil : Array(self.dropFirst())
}
}
public func check<A: Arbitrary & Smaller>(message: String, size: Int = 100, prop: (A) -> Bool) -> () {
for _ in 0..<size {
let value = A.arbitrary()
if !prop(value) {
let smallerValue = iterateWhile(condition: { !prop($0) }, initialValue: value) {
$0.smaller()
}
print("\"\(message)\" doesn't hold: \(smallerValue)")
return
}
}
print("\"\(message)\" passed \(size) tests.")
}
public func check<A: Arbitrary & Smaller, B: Arbitrary & Smaller>(message: String, size: Int = 100, prop: (A, B) -> Bool) -> () {
for _ in 0..<size {
let value0 = A.arbitrary()
let value1 = B.arbitrary()
if !prop(value0, value1) {
let smallerValue0 = iterateWhile(condition: { !prop($0, value1) }, initialValue: value0) {
$0.smaller()
}
let smallerValue1 = iterateWhile(condition: { !prop(smallerValue0, $0) }, initialValue: value1) {
$0.smaller()
}
print("\"\(message)\" doesn't hold: (\(smallerValue0), \(smallerValue1))")
return
}
}
print("\"\(message)\" passed \(size) tests.")
}
public func check<A: Arbitrary & Smaller, B: Arbitrary & Smaller, C: Arbitrary & Smaller>(message: String, size: Int = 100, prop: (A, B, C) -> Bool) -> () {
for _ in 0..<size {
let value0 = A.arbitrary()
let value1 = B.arbitrary()
let value2 = C.arbitrary()
if !prop(value0, value1, value2) {
let smallerValue0 = iterateWhile(condition: { !prop($0, value1, value2) }, initialValue: value0) {
$0.smaller()
}
let smallerValue1 = iterateWhile(condition: { !prop(smallerValue0, $0, value2) }, initialValue: value1) {
$0.smaller()
}
let smallerValue2 = iterateWhile(condition: { !prop(smallerValue0, smallerValue1, $0) }, initialValue: value2) {
$0.smaller()
}
print("\"\(message)\" doesn't hold: (\(smallerValue0), \(smallerValue1), \(smallerValue2))")
return
}
}
print("\"\(message)\" passed \(size) tests.")
}
public func check<A: Arbitrary & Smaller, B: Arbitrary & Smaller, C: Arbitrary & Smaller, D: Arbitrary & Smaller>(message: String, size: Int = 100, prop: (A, B, C, D) -> Bool) -> () {
for _ in 0..<size {
let value0 = A.arbitrary()
let value1 = B.arbitrary()
let value2 = C.arbitrary()
let value3 = D.arbitrary()
if !prop(value0, value1, value2, value3) {
let smallerValue0 = iterateWhile(condition: { !prop($0, value1, value2, value3) }, initialValue: value0) {
$0.smaller()
}
let smallerValue1 = iterateWhile(condition: { !prop(smallerValue0, $0, value2, value3) }, initialValue: value1) {
$0.smaller()
}
let smallerValue2 = iterateWhile(condition: { !prop(smallerValue0, smallerValue1, $0, value3) }, initialValue: value2) {
$0.smaller()
}
let smallerValue3 = iterateWhile(condition: { !prop(smallerValue0, smallerValue1, smallerValue2, $0) }, initialValue: value3) {
$0.smaller()
}
print("\"\(message)\" doesn't hold: (\(smallerValue0), \(smallerValue1), \(smallerValue2), \(smallerValue3))")
return
}
}
print("\"\(message)\" passed \(size) tests.")
}
public func qsort(_ array: [Int]) -> [Int] {
if array.isEmpty {
return []
}
var arr = array
let pivot = arr.removeFirst()
let lesser = arr.filter {
$0 < pivot
}
let greater = arr.filter {
$0 >= pivot
}
return qsort(lesser) + [pivot] + qsort(greater)
}
check(message: "qsort should behave like sort") { (x: [Int]) in
qsort(x) == x.sorted(by: <)
} |
@Guang1234567 You can work around that problem by passing a value argument additionally to the type argument to functions. For example, if you have an interface and a function that look like this: abstract class Bar {
void bar();
}
void foo<T: Bar>(T a) {
a.bar();
} In languages like Rust, you can implement abstract class BarImpl<T> {
void bar(T a);
}
void foo<T>(T a, BarImpl<T> impl) {
impl.bar(a);
} This code example is equivalent to the first except that you can also implement class IntBarImpl extends BarImpl<int> {
void bar(int a) => ...;
} And then use the function like this: foo(42, IntBarImpl()); Btw: Purely dynamic functional languages usually work like this. Because there's no type system for associating code with values, behavior needs to be passed around as values as well. Btw 2: I wrote a property-based testing framework called glados. |
I don‘t like the traditional unit test framework or library, event though it puts on the FP's new clothes. introduce of QuickyCheck is here. And above
|
My And as the existence of Also, I described above how every use case that involves an intricate type system like that can be solved without one. So there is no use case that's impossible to solve currently, only perhaps extra parameters need to be passed at the place of code where the type is specified. |
I see, thanks a lot. Just as glados#how-to-write-generators said: class User {
final String name;
final int age;
}
extension AnyUser on Any {
Generator<User> get user => combine3(any.string, any.int, (name, age) {
return User(name, age);
});
}
enum Ripeness { ripe, unripe }
extension AnyRipeness on Any {
Generator<Ripeness> get ripeness => choose(Ripeness.values);
} Create a global instance It's a wonderful solution for working around the problem of not having interfaces and static methods on extensions 👍 . |
Like in #281 based on extensions
The text was updated successfully, but these errors were encountered: