In Zig 0.9, I need a literal expression that has type void, for use as the context parameter to std.sort.sort, so that my lessThan function signature is semantically accurate. Is there some?
I tried these but to no avail:
const ek = #import("std").io.getStdOut().writer();
test "void" {
const kandidati = .{ type, u0, .{}, void, null, undefined };
inline for (kandidati) |k|
try ek.print("{}, ", .{#TypeOf(k)});
try ek.print("\n", .{});
}
giving
Test [0/1] test "void"... type, type, struct:31:37, type, #Type(.Null), #Type(.Undefined),
All 1 tests passed.
I don't want to use a dummy variable like const v: void = undefined;; that's too verbose.
For reference, using void as the context parameter to std.sort.sort with a lessThan function that takes a parameter of type void, gives an error message like
error: expected type 'fn(type,anytype,anytype) anytype', found 'fn(void, Type1, Type1) bool'
The expressions void{}, #as(void, undefined), and {} have type void, for example. You can see {} used in the standard library test cases for std.sort.sort.
The "canonical" way of getting the void value is to use an empty block.
Related
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
Function foo = (int x) => x;
if (foo is Function(Object)) {
print('Bar'); // Doesn't print
}
According to me, Function(int) is a subtype of Function(Object) since type int is subtype of type Object and hence the if condition should return true but it doesn't. Why is that so?
I wasn't getting any good title for this question, feel free to change it.
It is not.
The point of subtypes is that an instance of a subtype can be used everywhere an instance of the supertype is expected (also known as "substitutability").
A Function(Object) can be called with any object as argument. It can be called with both "foo" and 42 as arguments.
A Function(int) cannot be called with a string as argument. That means that a Function(int) cannot be substituted where a Function(Object) is expected, and the type system does indeed not make Function(int) a subtype of Function(Object).
It does make Function(Object) a subtype of Function(int) instead, because if a function which can be called with any argument, then it can be used anywhere a function accepting an int argument is needed - because it accepts an int.
In general, a R1 Function(P1) function type is a subtype of R2 Function(P2) if R1 is a subtype of R2 and P2 is a subtype of P1. Notice the reverse ordering of the parameter type. Parameter types are covariant.
Think of it as: A function can be used in a place where it's given an argument of type P and is expected to return a value of type R if (and only if) it accepts any P argument and possibly more, and the values it might return must all be R values but not necessarily all R values.
That is, a function type is a subtype of R Function(P) if it accepts at least P and returns at most R.
Though it's not immediately intuitive, the subtyping is the opposite: Function(Object) is a subtype of Function(int). Indeed, a type A can be a subtype of B only if anything that holds about B, also holds about A. Function(Object) can always be called with an int argument, but Function(int) cannot be called with an arbitrary Object, so:
Function foo = (Object x) => x;
if (foo is Function(int)) {
print('Bar'); // prints!
}
In other words, functions are contravariant in the parameter type.
I am trying to use an array of elements as union type, something that became easy with const assertions in TS 3.4, so I can do this:
const CAPITAL_LETTERS = ['A', 'B', 'C', ..., 'Z'] as const;
type CapitalLetter = typeof CAPITAL_LETTERS[string];
Now I want to test whether a string is a capital letter, but the following fails with "not assignable to parameter of type":
let str: string;
...
CAPITAL_LETTERS.includes(str);
Is there any better way to fix this rather than casting CAPITAL_LETTERS to unknown and then to Array<string>?
The standard library signature for Array<T>.includes(u) assumes that the value to be checked is of the same or narrower type than the array's elements T. But in your case you are doing the opposite, checking against a value which is of a wider type. In fact, the only time you would say that Array<T>.includes<U>(x: U) is a mistake and must be prohibited is if there is no overlap between T and U (i.e., when T & U is never).
Now, if you're not going to be doing this sort of "opposite" use of includes() very often, and you want zero runtime efects, you should just widen CAPITAL_LETTERS to ReadonlyArray<string> via type assertion:
(CAPITAL_LETTERS as ReadonlyArray<string>).includes(str); // okay
If, on the other hand, you feel seriously enough that this use of includes() should be accepted with no type assertions, and you want it to happen in all of your code, you could merge in a custom declaration:
// global augmentation needed if your code is in a module
// if your code is not in a module, get rid of "declare global":
declare global {
interface ReadonlyArray<T> {
includes<U>(x: U & ((T & U) extends never ? never : unknown)): boolean;
}
}
That will make it so that an array (well, a readonly array, but that's what you have in this example) will allow any parameter for .includes() as long as there is some overlap between the array element type and the parameter type. Since string & CapitalLetter is not never, it will allow the call. It will still forbid CAPITAL_LETTERS.includes(123), though.
Okay, hope that helps; good luck!
Another way to solve it is with a type guard
https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards
const myConstArray = ["foo", "bar", "baz"] as const
function myFunc(x: string) {
//Argument of type 'string' is not assignable to parameter of type '"foo" | "bar" | "baz"'.
if (myConstArray.includes(x)) {
//Hey, a string could totally be one of those values! What gives, TS?
}
}
//get the string union type
type TMyConstArrayValue = typeof myConstArray[number]
//Make a type guard
//Here the "x is TMyConstArrayValue" tells TS that if this fn returns true then x is of that type
function isInMyConstArray(x: string): x is TMyConstArrayValue {
return myConstArray.includes(x as TMyConstArrayValue)
//Note the cast here, we're doing something TS things is unsafe but being explicit about it
//I like to this of type guards as saying to TS:
//"I promise that if this fn returns true then the variable is of the following type"
}
function myFunc2(x: string) {
if (isInMyConstArray(x)) {
//x is now "foo" | "bar" | "baz" as originally intended!
}
}
While you have to introduce another "unnecessary" function this ends up looking clean and working perfectly. In your case you would add
const CAPITAL_LETTERS = ['A', 'B', 'C', ..., 'Z'] as const;
type CapitalLetter = typeof CAPITAL_LETTERS[string];
function isCapitalLetter(x: string): x is CapitalLetter {
return CAPITAL_LETTERS.includes(x as CapitalLetter)
}
let str: string;
isCapitalLetter(str) //Now you have your comparison
//Not any more verbose than writing .includes inline
if(isCapitalLetter(str)){
//now str is of type CapitalLetter
}
Here's a solution that works well for strings & string literals using TypeScript 4.1 Template Literal Types that doesn't break anything else, and also narrows the type for convenience when used in conditions:
declare global {
interface ReadonlyArray<T> {
includes<S, R extends `${Extract<S, string>}`>(
this: ReadonlyArray<R>,
searchElement: S,
fromIndex?: number
): searchElement is R & S;
}
}
Originally posted by noppa in a TypeScript github issue related to this.
Adding to #imagio's answer, you can make the genetic type guard (thanks to #wprl for simplification)
function isIn<T>(values: readonly T[], x: any): x is T {
return values.includes(x);
}
And use it with any as const array:
const specialNumbers = [0, 1, 2, 3] as const;
function foo(n: number) {
if (isIn(specialNumbers, n)) {
//TypeScript will say that `s` has type `0 | 1 | 2 | 3` here
}
}
You can also create a curried version of Array.prototype.includes which works with tuples:
const PROPS = ['a', 'b', 'c'] as const;
const withTuple = <
List extends string[]
>(list: readonly [...List]) =>
(prop: string): prop is List[number] =>
list.includes(prop)
const includes = withTuple(PROPS);
const result = includes('d')
declare let str: string
if (includes(str)) {
str // "a" | "b" | "c"
}
Playground
Higher order function with list argument created for inference.
You can also check my article
Reassignment using a wider type annotation is potentially the simplest solution, if a little untidy due to adding an extraneous variable.
const CAPITAL_LETTERS = ['A', 'B', 'C', ..., 'Z'] as const;
const widenedCapitalLetters: string[] = CAPITAL_LETTERS
widenedCapitalLetters.includes("hello")
This allows you to keep the const assertion on the base array so you get the type narrowing you need.
using lodash
const CAPITAL_LETTERS = ['A', 'B', 'C', 'Z'] as const;
_.includes(CAPITAL_LETTERS, 'A');
In Rust, I'm trying to create a list of callbacks functions to invoke later:
use std::vec::Vec;
fn add_to_vec<T: FnMut() -> ()>(v: &Vec<Box<FnMut() -> ()>>, f: T) {
v.push(Box::new(f));
}
fn call_b() {
println!("Call b.");
}
#[test]
fn it_works() {
let calls: Vec<Box<FnMut() -> ()>> = Vec::new();
add_to_vec(&calls, || { println!("Call a."); });
add_to_vec(&calls, call_b);
for c in calls.drain() {
c();
}
}
I'm mostly following the advice here on how to store a closure, however, I'm still seeing some errors:
src/lib.rs:6:12: 6:23 error: the parameter type `T` may not live long enough [E0311]
src/lib.rs:6 v.push(Box::new(f));
^~~~~~~~~~~
src/lib.rs:6:23: 6:23 help: consider adding an explicit lifetime bound for `T`
src/lib.rs:5:68: 7:2 note: the parameter type `T` must be valid for the anonymous lifetime #1 defined on the block at 5:67...
src/lib.rs:5 fn add_to_vec<T: FnMut() -> ()>(v: &Vec<Box<FnMut() -> ()>>, f: T) {
src/lib.rs:6 v.push(Box::new(f));
src/lib.rs:7 }
src/lib.rs:6:12: 6:23 note: ...so that the type `T` will meet its required lifetime bounds
src/lib.rs:6 v.push(Box::new(f));
^~~~~~~~~~~
I've tried changing the function signature to:
fn add_to_vec<'a, T: FnMut() -> ()>(v: &Vec<Box<FnMut() -> ()>>, f: &'a T) {
… but this gets me:
src/lib.rs:6:12: 6:23 error: the trait `core::ops::Fn<()>` is not implemented for the type `&T` [E0277]
src/lib.rs:6 v.push(Box::new(f));
^~~~~~~~~~~
error: aborting due to previous error
src/lib.rs:6:12: 6:23 error: the trait `core::ops::Fn<()>` is not implemented for the type `&T` [E0277]
src/lib.rs:6 v.push(Box::new(f));
^~~~~~~~~~~
src/lib.rs:18:24: 18:51 error: mismatched types:
expected `&_`,
found `[closure src/lib.rs:18:24: 18:51]`
(expected &-ptr,
found closure) [E0308]
src/lib.rs:18 add_to_vec(&calls, || { println!("Call a."); });
^~~~~~~~~~~~~~~~~~~~~~~~~~~
(The last error I can correct by adding a &; while I think this is something I should need, because add_to_vec is going to end up owning the closure, and thus needs to borrow it, I'm not entirely sure.)
There are a few problems with your code. Here’s a fully fixed version to begin with:
use std::vec::Vec;
fn add_to_vec<'a, T: FnMut() + 'a>(v: &mut Vec<Box<FnMut() + 'a>>, f: T) {
v.push(Box::new(f));
}
fn call_b() {
println!("Call b.");
}
#[test]
fn it_works() {
let mut calls: Vec<Box<FnMut()>> = Vec::new();
add_to_vec(&mut calls, || { println!("Call a."); });
add_to_vec(&mut calls, call_b);
for mut c in calls.drain() {
c();
}
}
The lifetime issue is that the boxed function objects must have a common base lifetime; if you just write the generic constraint T: FnMut(), it is assumed to only need to live as long as the function call and not any longer. Therefore two things need to be added to it all: the generic parameter T must be constrained to a specified lifetime, and in order to store it inside the vector, the trait object type must similarly be constrained, as Box<FnMut() + 'a>. That way they both match up and memory safety is ensured and so the compiler lets it through. The -> () part of FnMut() -> () is superfluous, by the way.
The remaining fixes that need to be made are the insertion of a few mut; in order to push to the vector, you naturally need a mutable reference, hence the & to &mut changes, and in order to take mutable references to calls and c the bindings must be made mut.
I started reading the Rust guide on closures. From the guide:
That is because in Rust each closure has its own unique type. So, not only do closures with different signatures have different types, but different closures with the same signature have different types, as well.
Is there a way to explicitly write the type signature of a closure? Is there any compiler flag that expands the type of inferred closure?
No. The real type of a closure is only known to the compiler, and it's not actually that useful to be able to know the concrete type of a given closure. You can specify certain "shapes" that a closure must fit, however:
fn call_it<F>(f: F)
where
F: Fn(u8) -> u8, // <--- HERE
{
println!("The result is {}", f(42))
}
fn main() {
call_it(|a| a + 1);
}
In this case, we say that call_it accepts any type that implements the trait Fn with one argument of type u8 and a return type of u8. Many closures and free functions can implement that trait however.
As of Rust 1.26.0, you can also use the impl Trait syntax to accept or return a closure (or any other trait):
fn make_it() -> impl Fn(u8) -> u8 {
|a| a + 1
}
fn call_it(f: impl Fn(u8) -> u8) {
println!("The result is {}", f(42))
}
fn main() {
call_it(make_it());
}
Quoting the reference, "A closure expression produces a closure value with a unique, anonymous type that cannot be written out".
However, under conditions defined by RFC1558, a closure can be coerced to a function pointer.
let trim_lines: fn((usize, &str)) -> (usize, &str) = |(i, line)| (i, line.trim());
Function pointers can be used in .map(), .filter(), etc just like a regular function. Types will be different but the Iterator trait will be present on the returned values.