How do I use a variable as the data type for a different variable? - variables

If I have the data type of something stored in the variable data_type, how can I create a new variable with the data type defined in this variable?
For example:
struct a {
var: String,
}
struct b {
var: String,
}
let var_type = "a";
let variable: var_type { var: "abc" }; // creates struct var_type

As long as you know all of your types at compile time, it is possible to transform unstructured data into typed data based on some value in the data. This is exactly what is done by the popular serde crate
Without knowing the use case, it's difficult to address the question precisely, yet the code below gives two examples about how to accomplish type-mapping using an enum (though match could be used to map any data to any type that is known at compile time).
enum VarType {
A(String),
B(String),
Unknown(String),
}
fn main() {
let _var1 = VarType::A("abc".to_string());
let _var2 = VarType::B("xyz".to_string());
let data = vec![("a", "abc"), ("b", "xyz")];
for item in data {
let (data_type, value) = item;
match data_type {
"a" => VarType::A(value.to_string()),
"b" => VarType::B(value.to_string()),
_ => VarType::Unknown(value.to_string()),
};
}
}

As Isak van Bakel, most said rust is static. However, if you have a list of all the possible structures, you can. (assuming your using serde here!). There is currently
a interesting question discussing polymorphic de-serialisation here, i suggest you take a look as it may help!

You can't. Rust is statically typed.

Related

Generic transpose (or anything else really!) in Kotlin

Working on an Advent of Code puzzle I had found myself defining a function to transpose matrices of integers:
fun transpose(xs: Array<Array<Int>>): Array<Array<Int>> {
val cols = xs[0].size // 3
val rows = xs.size // 2
var ys = Array(cols) { Array(rows) { 0 } }
for (i in 0..rows - 1) {
for (j in 0..cols - 1)
ys[j][i] = xs[i][j]
}
return ys
}
Turns out that in the following puzzle I also needed to transpose a matrix, but it wasn't a matrix of Ints, so i tried to generalize. In Haskell I would have had something of type
transpose :: [[a]] -> [[a]]
and to replicate that in Kotlin I tried the following:
fun transpose(xs: Array<Array<Any>>): Array<Array<Any>> {
val cols = xs[0].size
val rows = xs.size
var ys = Array(cols) { Array(rows) { Any() } } // maybe this is the problem?
for (i in 0..rows - 1) {
for (j in 0..cols - 1)
ys[j][i] = xs[i][j]
}
return ys
}
This seems ok but it isn't. In fact, when I try calling it on the original matrix of integers I get Type mismatch: inferred type is Array<Array<Int>> but Array<Array<Any>> was expected.
The thing is, I don't really understand this error message: I thought Any was a supertype of anything else?
Googling around I thought I understood that I should use some sort of type constraint syntax (sorry, not sure it's called like that in Kotlin), thus changing the type to fun <T: Any> transpose(xs: Array<Array<T>>): Array<Array<T>>, but then at the return line I get Type mismatch: inferred type is Array<Array<Any>> but Array<Array<T>> was expected
So my question is, how do I write a transpose matrix that works on any 2-dimensional array?
As you pointed out yourself, the line Array(cols) { Array(rows) { Any() } } creates an Array<Array<Any>>, so if you use it in your generic function, you won't be able to return it when Array<Array<T>> is expected.
Instead, you should make use of this lambda to directly provide the correct value for the correct index (instead of initializing to arbitrary values and replacing all of them):
inline fun <reified T> transpose(xs: Array<Array<T>>): Array<Array<T>> {
val cols = xs[0].size
val rows = xs.size
return Array(cols) { j ->
Array(rows) { i ->
xs[i][j]
}
}
}
I don't really understand this error message: I thought Any was a supertype of anything else?
This is because arrays in Kotlin are invariant in their element type. If you don't know about generic variance, it's about describing how the hierarchy of a generic type compares to the hierarchy of their type arguments.
For example, assume you have a type Foo<T>. Now, the fact that Int is a subtype of Any doesn't necessarily imply that Foo<Int> is a subtype of Foo<Any>. You can look up the jargon, but essentially you have 3 possibilities here:
We say that Foo is covariant in its type argument T if Foo<Int> is a subtype of Foo<Any> (Foo types "vary the same way" as T)
We say that Foo is contravariant in its type argument T if Foo<Int> is a supertype of Foo<Any> (Foo types "vary the opposite way" compared to T)
We say that Foo is invariant in its type argument T if none of the above can be said
Arrays in Kotlin are invariant. Kotlin's read-only List, however, is covariant in the type of its elements. This is why it's ok to assign a List<Int> to a variable of type List<Any> in Kotlin.

Flatbuffers: How to allow multiple types for a single field

I'm writing a communication protocol schema for a list of parameters which can be of multiple values: uint64, float64, string or bool.
How can I set a table field to a union of multiple primitive scalar & non-scalar primitive type?
I've already tried using a union of those types, but I end up with the following error when building:
$ schemas/foobar.fbs:28: 0: error: type referenced but not defined
(check namespace): uint64, originally at: schemas/request.fbs:5
Here's the schema in its current state:
namespace Foobar;
enum RequestCode : uint16 { Noop, Get, Set, BulkGet, BulkSet }
union ParameterValue { uint64, float64, bool, string }
table Parameter {
name:string;
value:ParameterValue;
unit:string;
}
table Request {
code:RequestCode = Noop;
payload:[Parameter];
}
table Result {
request:Request;
success:bool = true;
payload:[Parameter];
}
The end result I'm looking for is the Request and Result tables to contain a list of parameters, where a parameter contains a name and value, and optionally the units.
Thx in advance!
Post-answer solution:
Here's what I came up with in the end, thx to Aardappel.
namespace foobar;
enum RequestCode : uint16 { Noop, Get, Set, BulkGet, BulkSet }
union ValueType { UnsignedInteger, SignedInteger, RealNumber, Boolean, Text }
table UnsignedInteger {
value:uint64 = 0;
}
table SignedInteger {
value:int64 = 0;
}
table RealNumber {
value:float64 = 0.0;
}
table Boolean {
value:bool = false;
}
table Text {
value:string (required);
}
table Parameter {
name:string (required);
valueType:ValueType;
unit:string;
}
table Request {
code:RequestCode = Noop;
payload:[Parameter];
}
table Result {
request:Request (required);
success:bool = true;
payload:[Parameter];
}
You currently can't put scalars directly in a union, so you'd have to wrap these in a table or a struct, where struct would likely be the most efficient, e.g.
struct UInt64 { u:uint64 }
union ParameterValue { UInt64, Float64, Bool, string }
This is because a union must be uniformly the same size, so it only allows types to which you can have an offset.
Generally though, FlatBuffers is a strongly typed system, and the schema you are creating here is undoing that by emulating dynamically typed data, since your data is essentially a list of (string, any type) pairs. You may be better off with a system designed for this particular use case, such as FlexBuffers (https://google.github.io/flatbuffers/flexbuffers.html, currently only C++) which explicitly has a map type that is all string -> any type pairs.
Of course, even better is to not store data so generically, but instead make a new schema for each type of request and response you have, and make parameter names into fields, rather than serialized data. This is by far the most efficient, and type safe.

how to create and export dynamic operators

I have some classes (and will need quite a few more) that look like this:
use Unit;
class Unit::Units::Ampere is Unit
{
method TWEAK { with self {
.si = True;
# m· kg· s· A ·K· mol· cd
.si-signature = [ 0, 0, 0, 1, 0, 0, 0 ];
.singular-name = "ampere";
.plural-name = "ampere";
.symbol = "A";
}}
sub postfix:<A> ($value) returns Unit::Units::Ampere is looser(&prefix:<->) is export(:short) {
return Unit::Units::Ampere.new( :$value );
};
sub postfix:<ampere> ($value) returns Unit::Units::Ampere is looser(&prefix:<->) is export(:long) {
$value\A;
};
}
I would like to be able to construct and export the custom operators dynamically at runtime. I know how to work with EXPORT, but how do I create a postfix operator on the fly?
I ended up basically doing this:
sub EXPORT
{
return %(
"postfix:<A>" => sub is looser(&prefix:<->) {
#do something
}
);
}
which is disturbingly simple.
For the first question, you can create dynamic subs by returning a sub from another. To accept only an Ampere parameter (where "Ampere" is chosen programmatically), use a type capture in the function signature:
sub make-combiner(Any:U ::Type $, &combine-logic) {
return sub (Type $a, Type $b) {
return combine-logic($a, $b);
}
}
my &int-adder = make-combiner Int, {$^a + $^b};
say int-adder(1, 2);
my &list-adder = make-combiner List, {(|$^a, |$^b)};
say list-adder(<a b>, <c d>);
say list-adder(1, <c d>); # Constraint type check fails
Note that when I defined the inner sub, I had to put a space after the sub keyword, lest the compiler think I'm calling a function named "sub". (See the end of my answer for another way to do this.)
Now, on to the hard part: how to export one of these generated functions? The documentation for what is export really does is here: https://docs.perl6.org/language/modules.html#is_export
Half way down the page, they have an example of adding a function to the symbol table without being able to write is export at compile time. To get the above working, it needs to be in a separate file. To see an example of a programmatically determined name and programmatically determined logic, create the following MyModule.pm6:
unit module MyModule;
sub make-combiner(Any:U ::Type $, &combine-logic) {
anon sub combiner(Type $a, Type $b) {
return combine-logic($a, $b);
}
}
my Str $name = 'int';
my $type = Int;
my package EXPORT::DEFAULT {
OUR::{"&{$name}-eater"} := make-combiner $type, {$^a + $^b};
}
Invoke Perl 6:
perl6 -I. -MMyModule -e "say int-eater(4, 3);"
As hoped, the output is 7. Note that in this version, I used anon sub, which lets you name the "anonymous" generated function. I understand this is mainly useful for generating better stack traces.
All that said, I'm having trouble dynamically setting a postfix operator's precedence. I think you need to modify the Precedence role of the operator, or create it yourself instead of letting the compiler create it for you. This isn't documented.

How to create a variable that can take strings and functions in Kotlin?

Is there a way to create a variable to store strings and functions? Like var x:dynamic where x can be any type or a function: x="foo"; x= {print (...)}
dynamic isn't a type (it just turns off type checking) and works only in kotlin.js (JavaScript). Is there a type that includes function types and Any?
I try this code and works fine.
The var x is Any so it can hold any kind of data (not nullable ) in it. To hold nullable data use Any?
var x: Any = "foo"
println( x )
x = { println("") }
x.invoke()
The IDE smart cast the variable but you can help the cast using this
(x as ()->Unit).invoke()

How can I tell Kotlin that an array or collection cannot contain nulls?

If I create an array, then fill it, Kotlin believes that there may be nulls in the array, and forces me to account for this
val strings = arrayOfNulls<String>(10000)
strings.fill("hello")
val upper = strings.map { it!!.toUpperCase() } // requires it!!
val lower = upper.map { it.toLowerCase() } // doesn't require !!
Creating a filled array doesn't have this problem
val strings = Array(10000, {"string"})
val upper = strings.map { it.toUpperCase() } // doesn't require !!
How can I tell the compiler that the result of strings.fill("hello") is an array of NonNull?
A rule of thumb: if in doubts, specify the types explicitly (there is a special refactoring for that):
val strings1: Array<String?> = arrayOfNulls<String>(10000)
val strings2: Array<String> = Array(10000, {"string"})
So you see that strings1 contains nullable items, while strings2 does not. That and only that determines how to work with these arrays:
// You can simply use nullability in you code:
strings2[0] = strings1[0]?.toUpperCase ?: "KOTLIN"
//Or you can ALWAYS cast the type, if you are confident:
val casted = strings1 as Array<String>
//But to be sure I'd transform the items of the array:
val asserted = strings1.map{it!!}
val defaults = strings1.map{it ?: "DEFAULT"}
Why the filled array works fine
The filled array infers the type of the array during the call from the lambda used as the second argument:
val strings = Array(10000, {"string"})
produces Array<String>
val strings = Array(10000, { it -> if (it % 2 == 0) "string" else null })
produces Array<String?>
Therefore changing the declaration to the left of the = that doesn't match the lambda does not do anything to help. If there is a conflict, there is an error.
How to make the arrayOfNulls work
For the arrayOfNulls problem, they type you specify to the call arrayOfNulls<String> is used in the function signature as generic type T and the function arrayOfNulls returns Array<T?> which means nullable. Nothing in your code changes that type. The fill method only sets values into the existing array.
To convert this nullable-element array to non-nullable-element list, use:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.filterNotNull()
val upper = strings.map { it.toUpperCase() } // no !! needed
Which is fine because your map call converts to a list anyway, so why not convert beforehand. Now depending on the size of the array this could be performant or not, the copy might be fast if in CPU cache. If it is large and no performant, you can make this lazy:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.asSequence().filterNotNull()
val upper = strings.map { it.toUpperCase() } // no !! needed
Or you can stay with arrays by doing a copy, but really this makes no sense because you undo it with the map:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings: Array<String> = Array(nullableStrings.size, { idx -> nullableStrings[idx]!! })
Arrays really are not that common in Java or Kotlin code (JetBrains studied the statistics) unless the code is doing really low level optimization. It could be better to use lists.
Given that you might end up with lists anyway, maybe start there too and give up the array.
val nullableStrings = listOf("a","b",null,"c",null,"d")
val strings = nullableStrings.filterNotNull()
But, if you can't stop the quest to use arrays, and really must cast one without a copy...
You can always write a function that does two things: First, check that all values are not null, and if so then return the array that is cast as not null. This is a bit hacky, but is safe only because the difference is nullability.
First, create an extension function on Array<T?>:
fun <T: Any> Array<T?>.asNotNull(): Array<T> {
if (this.any { it == null }) {
throw IllegalStateException("Cannot cast an array that contains null")
}
#Suppress("CAST_NEVER_SUCCEEDS")
return this as Array<T>
}
Then use this function new function to do the conversion (element checked as not null cast):
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.asNotNull() // magic!
val upperStrings = strings.map { it.toUpperCase() } // no error
But I feel dirty even talking about this last option.
There is no way to tell this to the compiler. The type of the variable is determined when it is declared. In this case, the variable is declared as an array that can contain nulls.
The fill() method does not declare a new variable, it only modifies the contents of an existing one, so it cannot cause the variable type to change.