Is there a generic Type in Dart like Class<T> in Java/Kotlin? - dart

In Kotlin I can do something like:
var myType : KClass<String>? = null
and can assign to it like:
myType = String::class
but NOT like:
myType = Int::class // Type mismatch: inferred type in KClass<Int> but KClass<String>? was expected
Is there something similar in Dart? I know of the Type type but it is not generic and while it can represent String or List<int> I seem not to be able to write similar code as my Kotlin example:
Type? t = null;
I can assign to it:
t = String;
AND also:
t = int;
but I want the second example to fail compilation. I would need some kind of Type<String>. Is this possible in Dart?

The Type class is not generic, and doesn't support subtype checks (or any other reasonable type-related operation). There is no way to use it for what you are trying to do.
So, don't. It's useless anyway. However, in Dart you can create your own type representation that is actually useful, because Dart doesn't erase type arguments, and you can then ask people using your code to ass that instead.
Say:
class MyType<T> implements Comparable<MyType>{ // Or a better name.
const MyType();
Type get type => T;
bool operator >=(MyType other) => other is MyType<T>;
bool operator <=(MyType other) => other >= this;
bool isInstance(Object? object) => object is T;
R runWith<R>(R Function<T>() action) => action<T>();
#override
int get hashCode => T.hashCode;
#override
bool operator==(Object other) => other is MyType && T == other.type;
}
With that you can write:
MyType<String?> type;
type = MyType<Null>(); // valid
type = MyType<String>(); // valid
type = MyType<Never>(); // valid
type = MyType<int>; // EEEK! compile-time error
You can use it where you need to store a type as a value.
The thing is, most of the time you can just use a type variable instead ,and creating an actual value to represent a type is overkill.
So, first try to just use a type parameter, instead of passing around Type or MyType objects. Only if that fails should you consider using MyType. Using Type is probably a mistake since it's not good for anything except doing == checks, which is antithetical to object orientation's idea of subtype subsumption.

I think this is the best you can get :
void main() {
aFunction<String>(String, '');
aFunction<String>(String, 1);
}
void aFunction<V>(Type type, V value) {
print(value.toString());
}
if you run this in a dartpad, you will see that
aFunction<String>(type, 1);
Doesn't compile.
But that's not really efficient because the type isn't guessed by Dart, you have to specify the generic type by hand.
I'm using Dart 2.17

Related

Comparing two types in Dart

I'm looking for a way to compare two types in Dart, and get True when they are the same.
I have a method:
SomeItemType exampleMethod<SomeItemType>(Map<String, dynamic> value) {
if (SomeItemType.toString() == 'BuiltMap<String, String>') {
return BuiltMap<String, String>.from(value) as SomeItemType;
}
// (...)
}
in which I must check if the type-parameter SomeItemType is actually equal to the type BuiltMap<String, String> or not.
The only way I found so far is the above string comparions, which is not too elegant in my opinion.
I have tried to following solutions, but they did not work out for me:
SomeItemType is BuiltMap<String, String>
// returns False
SomeItemType == BuiltMap<String, String>
// error: The operator '<' isn't defined for the type 'Type'.
BuiltMap<String, String> is SomeItemType
// error: The operator '<' isn't defined for the type 'Type'.
You need to detect whether a type variable holds a type which is "the same" as some known type.
I'd probably not go for equality in that check, I'd just check if the type of the variable is a subtype of the known type.
You can do that like:
class _TypeHelper<T> {}
bool isSubtypeOf<S, T>() => _TypeHelper<S>() is _TypeHelper<T>;
// or just ... => <S>[] is List<T>;
...
if (isSubtypeOf<T, BuiltMap<String, String>>()) { ... }
If you want to check type equality, then you have to decide what type equality means.
The simplest is "mutual subtyping", which would mean:
bool isMutualSubtypes<S, T>() => isSubtypeOf<S, T>() && isSubtypeOf<T, S>();
// or => isSubtypeOf<S Function(S), T Function(T)>();
// or => isSubtypeOf<void Function<X extends T>() Function(),
// void Function<X extends S>() Function()>();
This will accept, e.g., isMutualSubtypes<void, dynamic>() because both types are top types.
Type identity is much harder to check because the language itself doesn't have that notion. If types are mutual subtypes, they are considered equivalent at run-time (we distinguish void and dynamic statically, but not at run-time).
You can try something like:
bool isSameType<S, T>() => S == T;
but equality of Type objects is very underspecified, and not guaranteed to work any better than mutual subtyping.
Definitely don't use string comparison:
// Don't use!
bool isSameType<S, T>() => "$S" == "$T"; // BAD!
because it's possible to have different types with the same name.
I needed to check if a variable pointed to a function. This worked for me variable is function - the word is

F# passing in a C# method into another C# method expecting Func<> parameter

In a c# dll I have a method, that takes func parameters:
public static AttrDiffRule Create<T>(string a_attr, string b_attr, Func<IAttrProxy,IAttrProxy,T,bool> parametricRule, T ruleParam, string desc = null)
and some predefined default methods intended for it:
public static bool NumberEqualsWithTolerance(IAttrProxy a, IAttrProxy b, double tolerance)
Now when using this in C#, I can write the following and it works:
var tmp = DefaultRules.Create("fds", "fds", DefaultRules.NumberEqualsWithTolerance, 10.0);
But, in F# this:
let attrRule = DefaultRules.Create("fd","fdsa", DefaultRules.NumberEqualsWithTolerance, 89.)
gives syntax error: "Error FS0002 This function takes too many arguments, or is used in a context where a function is not expected"
What would be the correct way to pass a C# static method into a parameter expecting a Func<> in F#?
It is important to actually pass in the function, and not a lambda wrapper, because the Create method's job is to use the argument function's MethodInfo, which gets hidden by the lambda wrapper's one.
The passed in function does not have overloads, also tried with specifying the type in place like
(DefaultRules.NumberEqualsWithTolerance : Func<IAttrProxy,IAttrProxy,float,bool>)
This is a case of F# being very thoughtful on your behalf - by helping you write more idiomatic F#.
In .NET, you are not actually passing in the function, as if it's a member reference, rather you are passing in a delegate object of type Func<>. The construction of the delegate object is done implicitly by C# when it has the necessary type information.
We can see this more clearly if we refactor this into an actual delegate type:
public delegate bool ParametricRule<T>(IAttrProxy a, IAttrProxy b, T value);
public static AttrDiffRule Create<T>(string a_attr, string b_attr, ParametricRule<T> parametricRule, T ruleParam, string desc = null)
{
return default;
}
If you try to construct a ParametricRule in F#, you'll see that its type is:
ParametricRule(IAttrProxy -> IAttrProxy -> 'a -> bool)
The rationale is that this way you can use regular F# functions, instead of some un-F#ish tupled input function. And this why it doesn't work in your case.
Because you're trying to throw the tupled version from C# right back at it.
So if you refactor your C# implementation to:
protected static bool NumberEqualsWithToleranceImpl(IAttrProxy a, IAttrProxy b, float tolerance)
{
return default;
}
public static ParametricRule<float> NumberEqualsWithTolerance => NumberEqualsWithToleranceImpl;
you'll see that it works like you'd expect it to, both from F# and C#.
let attrRule = DefaultRules.Create("fd","fdsa", DefaultRules.NumberEqualsWithTolerance, 89.0f) //compiles, yay!
Sometimes the type resolution has trouble when passing a method as a function parameter, because there can be overloads on the method that make the signature ambiguous. You can just wrap the function in a lambda that passes the parameters.
let attrRule =
DefaultRules.Create(
"fd",
"fdsa",
(fun a b tolerance -> DefaultRules.NumberEqualsWithTolerance(a, b, tolerance)),
89.0)

When to use F#'s typedefof<'T> vs. typeof<'T>?

Can someone clarify when to use typedefof<'T> vs. typeof<'T>?
Both typedefof<System.String> and typeof<System.String> return the same Type instance.
However, they return different instances and different information for System.Collections.Generic.List<_>.
Can I think of typedefof as a new and improved typeof? Should I just switch to always using typedefof? Or is it more subtle than that?
This ought to illustrate the difference. When you use typeof, the compiler infers type arguments and constructs a concrete type. In this case, the inferred type argument is System.Object:
let t1 = typeof<System.Collections.Generic.List<_>>
let t2 = typedefof<System.Collections.Generic.List<_>>
printfn "t1 is %s" t1.FullName
printfn "t2 is %s" t2.FullName
Output:
t1 is System.Collections.Generic.List`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
t2 is System.Collections.Generic.List`1
Because typeof can only return a constructed type, typedefof is necessary if you need a type object representing a generic type definition.
typeof is used when you want to get the System.Type object for a given type. typedefof is used when you want to get the System.Type that represents the type definition for a generic type. As an example that uses both, suppose you had a type called Generic<'a>, and you wanted to create a function that returned the System.Type object for the Generic of any given type.
type Generic<'a> = Value of 'a
let makeGenericOf<'a> () =
typedefof<Generic<_>>.MakeGenericType(typeof<'a>)
Here, you would use the typedefof function to get the type defintion, and typeof to get the type of 'a for constructing the generic Generic<'a> Type.
I really appreciate the answers from phoog, Aaron, and JLRishe. Here is what I have learned, based on their answers and my own experimentation.
There are two Type instances associated with generics.
There is a Type associated with a generic that has specific type parameters. For example, there is a Type associated with List<int> and a different Type associated with List<string>. This is what you get when you use typeof<>.
> typeof<List<string>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.String]"
> typeof<List<int>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.Int32]"
There is a Type associated with the generic type definition itself. For example, there is a single Type associated with List<'T>, which is the same for List<int>, List<string>, and List<_>. This is what you get when you use typedefof<>.
> typedefof<List<string>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
> typedefof<List<int>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
> typedefof<List<_>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
By the way, the Type class has an instance method to GetGenericTypeDefinition(). That means, the following two return the same instance:
> Object.ReferenceEquals(typeof<List<int>>.GetGenericTypeDefinition(), typedefof<List<int>>);;
val it : bool = true
What happens if you call typeof<List<_>>? You get back the Type definition for List<Object>, as phoog mentioned.
> typeof<List<_>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.Object]"
This is all helpful to understand. For example, suppose I need to know if an object is a generic list (of any type).
// does not give me the answer I naively expected
> o.GetType() = typeof<List<_>>;;
val it : bool = false
// does this reference point to a List<'T>?
> o.GetType().IsGenericType && o.GetType().GetGenericTypeDefinition() = typedefof<List<_>>;;
val it : bool = true
Additionally, if you want to late-bound instantiate a generic type, you can use the MakeGenericType(...) method which Aaron mentioned.
> let myList = typedefof<List<_>>.MakeGenericType(typeof<int>);;
val myList : Type = Microsoft.FSharp.Collections.FSharpList`1[System.Int32]

Dapper column to F# option property

How do I get Dapper to convert data to an F# type with an option property? Simple example:
type SomeType = {
Count : int option
}
connection.QueryAsync<SomeType>(...)
This throws:
System.InvalidOperationException
A parameterless default constructor or one matching signature
(System.Int32 count) is required for SomeType materialization
Using Nullable instead of option works:
type SomeType = {
Count : Nullable<int>
}
But it's not as desirable for various reasons. For one thing, I have cases where I use string option (because F# doesn't allow null assignments normally, which is great), and Nullable<string> doesn't compile. Is there a way to configure/instruct Dapper to automatically deal with option types? I'd like to avoid using custom mappings every time.
In case it matters, using with Npgsql.
I don't think there's any support for adding handlers for open generics, so you'd have to add a type handler for each type you need.
You could define a handler like this:
type OptionHandler<'T>() =
inherit SqlMapper.TypeHandler<option<'T>>()
override __.SetValue(param, value) =
let valueOrNull =
match value with
| Some x -> box x
| None -> null
param.Value <- valueOrNull
override __.Parse value =
if isNull value || value = box DBNull.Value
then None
else Some (value :?> 'T)
And register for the types you need like this:
SqlMapper.AddTypeHandler (OptionHandler<string>())
SqlMapper.AddTypeHandler (OptionHandler<int>())

The type checker doesn't like callable objects implementing noSuchMethod

The following code works:
typedef num MyFunc(num);
class ObjectThatIsLikeFunc {
call(x) => x;
}
var obj = new ObjectThatIsLikeFunc();
MyFunc g = obj; //works
If, however, ObjectThatIsLikeFunc doesn't have the call method, but defines noSuchMethod instead, it doesn't work.
typedef num MyFunc(num);
class ObjectThatIsLikeFunc {
noSuchMethod(InvocationMirror) => 100;
}
I'm getting "is not a subtype of type 'MyFunc'".
My Question:
Is there a way to tell the type checker that ObjectThatIsLikeFunc with noSuchMethod can act as MyFunc?
Short answer, not that I'm aware of. The generalized case is, "how can I have a class that implements noSuchMethod act like any type?" I think I heard some talk of how Dart might allow this, but I couldn't find a reference to it.

Resources