return type based on content of an array
Hi, I'm going circles... ;) I read a string that contains an array of unknown dimension like: a = [1,2,3,4] or a = [[1,2],[3,4]] or a = [[[1,2],[3,4]],[[5,6],[7,8]]] With that I want to perform specified operations e.g. also provided on command line. Because at compile time the dimension of the array is unkown I did not find any possibility to do something like: auto arr = func(a); where 'auto func(string str)' should return either a 1D, 2D, 3D array and arr is not locked in an if(){} scope. Is there any way and What should I'm looking for? Thanks, thorstein
Re: Manually allocating a File
On Wednesday, February 21, 2018 02:59:21 Nicholas Wilson via Digitalmars-d- learn wrote: > On Tuesday, 20 February 2018 at 15:32:45 UTC, Chris M. wrote: > > Thanks for the info, that clears things up. Like I said, it was > > more experimentation rather than me planning to actually use > > it. Works now with the following modifications. > > > > import std.stdio; > > import core.stdc.stdlib; > > import std.conv; > > > > void main() > > { > > > > auto f = cast(File*) malloc(File.sizeof); > > emplace!File(f, "test.txt", "r"); > > f.readln.write; > > free(f); > > > > } > > > >>> (*f).readln.writeln; > >> > >> That * is unnecessary btw, in D you can > >> > >> f.readln > >> > >> and it will see it is a pointer to struct and dereference it > >> for you. > > > > That's what I had originally, but I put the * in to see if it > > would help with the original issue and never removed it > > FYI, File is a reference counted FILE* so theres not really any > point of heap allocating it. And worse, simply calling free does not run the File's destructor, so simply using malloc to allocate it and free to free is not going to result in File functioning properly. If someone were going to just use the C API and not use std.stdio.File, then using malloc and free with FILE might make sense, but it really doesn't make sense with File. - Jonathan M Davis
Re: Manually allocating a File
On Tuesday, 20 February 2018 at 15:32:45 UTC, Chris M. wrote: Thanks for the info, that clears things up. Like I said, it was more experimentation rather than me planning to actually use it. Works now with the following modifications. import std.stdio; import core.stdc.stdlib; import std.conv; void main() { auto f = cast(File*) malloc(File.sizeof); emplace!File(f, "test.txt", "r"); f.readln.write; free(f); } (*f).readln.writeln; That * is unnecessary btw, in D you can f.readln and it will see it is a pointer to struct and dereference it for you. That's what I had originally, but I put the * in to see if it would help with the original issue and never removed it FYI, File is a reference counted FILE* so theres not really any point of heap allocating it.
Re: Link to https://run.dlang.io/ ??
On Tuesday, 20 February 2018 at 14:45:55 UTC, Dennis wrote: I recently tried to go to that site, and I tried `run.dlang.com` which is the wrong URL. So I was looking through the D homepage for the right link but couldn't find it. Even a Google search for "online d compiler" or "run dlang online" didn't turn up anything directly, I had to go through the "Online compilers" page on the D wiki to finally get the right URL. And I already knew it existed. Putting more links to it definitely seems like a good idea since it's such a useful tool. Thanks! We're on it: https://github.com/dlang/dlang.org/pull/2147 - more input to this PR (or the front page in general is always welcome)
Re: Capturing by reference with "visit" from std.variant
On Tuesday, 20 February 2018 at 16:20:45 UTC, Smaehtin wrote: On Tuesday, 20 February 2018 at 16:15:56 UTC, Radu wrote: On Tuesday, 20 February 2018 at 16:01:11 UTC, Smaehtin wrote: I'm trying to understand why the following doesn't work: import std.stdio; import std.variant; void main() { Algebraic!(string, int) test = "Test"; test.tryVisit!( (ref string s) { s = "Why does this not work?"; } ); writeln(test); } But this works fine: *test.peek!string = "Works fine"; As far as I can tell, the "visit" template expands to something that ends up calling my handler like this: if (auto ptr = variant.peek!T) { handler(*ptr); } But seeing as the handler in my case takes a reference, shouldn't that work just fine? What am I missing? Your lambda is called, but you can't change the variant value trough that reference. Test with: import std.stdio; import std.variant; void main() { Algebraic!(string, int) test = "Test"; test.tryVisit!( (ref string s) { writeln("Why does this work? ", s); } ); writeln(test); } You should get: Why does this work? Test Test Then my question is: Why can't I change the variant through the reference in the lambda? Seeing as this works just fine: void main() { Algebraic!(string, int) test = "Test"; changeIt(*test.peek!string); writeln(test); } void changeIt(ref string s) { s = "Changed"; } Prints: Changed Okay, so I realized this happens because the variant parameter is getting passed down to the "visitImpl" function by value. Wouldn't it make sense to pass the arguments using "auto ref" in this case? This is the code I'm talking about: https://github.com/dlang/phobos/blob/9021bd36b97d247cba1def9ce12eca64efee61a5/std/variant.d#L2170 Changing it to "auto ref" would allow capturing by reference in the handlers, but I'm not really sure if it would have any negative side-effects.
Re: Can someone help a Reddit user
On 2/20/18 2:00 PM, bachmeier wrote: Someone has posted a question on our subreddit. Would be nice if he could get an answer: https://www.reddit.com/r/d_language/comments/7yxwvm/why_do_my_threads_write_to_the_wrong_file/ I responded. Looks to me like a race condition in the DMC libc code, but I don't have an environment set up to test at the moment. -Steve
Can someone help a Reddit user
Someone has posted a question on our subreddit. Would be nice if he could get an answer: https://www.reddit.com/r/d_language/comments/7yxwvm/why_do_my_threads_write_to_the_wrong_file/
Re: Trying to forward unwrapped opDispatch names to alias this
On Tuesday, 20 February 2018 at 16:12:17 UTC, Adam D. Ruppe wrote: On Monday, 19 February 2018 at 08:28:22 UTC, aliak wrote: T is the wrapped type. So if T has a member (in the example it's the built in field "max") then forward that. Oh, I see what you mean. So the problem is that built in types don't have "members" per se, they have "magic". The built in properties don't count to `hasMember`. You could probably do `if(__traits(compiles, mixin("a." ~ member))` though. I assume this should work because rules for alias this (as I understand) are to basically try if there's a member name that resolves the call, else forward to alias this. yeah, just built in properties/members don't pass the same existence checks so that's confusing your template constraint. I believe the same happens if it's not a built in property. You can replace B!int with B!S and have S contains a function "f" (for instance), then whether or the not constraint on opDispatch is: __traits(compiles, mixin("t." ~ name)) or hasMember(T, name) You still get: b.p.writeln; // Error: no property 'p' for type 'B!(S)'
Re: Trying to forward unwrapped opDispatch names to alias this
On Tuesday, 20 February 2018 at 11:27:23 UTC, Alex wrote: There is a related ticket, https://issues.dlang.org/show_bug.cgi?id=6434 However, not exactly facing this question. Should that ticket be marked as resolved? The issue is for alias this to be considered before opDispatch but there were no arguments after giving reasons for that being a bad idea. And opDispatch is considered before (and disables alias this) now it seems? It seems, that currently opDispatch replaces alias this... (?) Nevertheless, not only T is the wrapped type, but also A with the alias. What to do, if both members have a property with the same name? And why? Good question, should it be dealt with in the same way as this is dealt with? struct S { void f() {writeln("S");} } struct A { S s; alias s this; void f() {writeln("A");} } A().f; // prints "A" However, the above behavior seems dangerous maybe and could lead to silent bugs. I'm thinking if A.f was not defined, then A().f would print "S". But then someone comes along and defines an A.f and the user of A now gets different functionality without even knowing about it. Hrm.. one is not sure how one feels :p But how about if they do not have the same name? Currently AliasThis says: "If the member is a class or struct, undefined lookups will be forwarded to the AliasThis member." So opDispatch, even though it's constrained, is not considered undefined if it doesn't pass. In those cases should it not be forwarded to the alias this? If not, then why does a constrained function not qualify as being undefined if the constraint doesn't pass? Consider: struct A { void defined() {} } struct B { void opDispatch(string name)() if (name == "defined") {} } void main() { A().defined; // ok B().defined; // ok A().undefined; B().undefined; } Errors are: * Error: no property 'undefined' for type 'A', did you mean 'defined'? * Error: no property 'undefined' for type 'B' If you then define a struct S that contains a function named "undefined" and add a "S s; alias s this;" inside A and B, you get: * Error: no property 'undefined' for type 'B' But not for A. Shouldn't the behavior here be consistent? Or why not? Churr, - Ali
Re: Capturing by reference with "visit" from std.variant
On Tuesday, 20 February 2018 at 16:15:56 UTC, Radu wrote: On Tuesday, 20 February 2018 at 16:01:11 UTC, Smaehtin wrote: I'm trying to understand why the following doesn't work: import std.stdio; import std.variant; void main() { Algebraic!(string, int) test = "Test"; test.tryVisit!( (ref string s) { s = "Why does this not work?"; } ); writeln(test); } But this works fine: *test.peek!string = "Works fine"; As far as I can tell, the "visit" template expands to something that ends up calling my handler like this: if (auto ptr = variant.peek!T) { handler(*ptr); } But seeing as the handler in my case takes a reference, shouldn't that work just fine? What am I missing? Your lambda is called, but you can't change the variant value trough that reference. Test with: import std.stdio; import std.variant; void main() { Algebraic!(string, int) test = "Test"; test.tryVisit!( (ref string s) { writeln("Why does this work? ", s); } ); writeln(test); } You should get: Why does this work? Test Test Then my question is: Why can't I change the variant through the reference in the lambda? Seeing as this works just fine: void main() { Algebraic!(string, int) test = "Test"; changeIt(*test.peek!string); writeln(test); } void changeIt(ref string s) { s = "Changed"; } Prints: Changed
Re: Capturing by reference with "visit" from std.variant
On Tuesday, 20 February 2018 at 16:01:11 UTC, Smaehtin wrote: I'm trying to understand why the following doesn't work: import std.stdio; import std.variant; void main() { Algebraic!(string, int) test = "Test"; test.tryVisit!( (ref string s) { s = "Why does this not work?"; } ); writeln(test); } But this works fine: *test.peek!string = "Works fine"; As far as I can tell, the "visit" template expands to something that ends up calling my handler like this: if (auto ptr = variant.peek!T) { handler(*ptr); } But seeing as the handler in my case takes a reference, shouldn't that work just fine? What am I missing? Your lambda is called, but you can't change the variant value trough that reference. Test with: import std.stdio; import std.variant; void main() { Algebraic!(string, int) test = "Test"; test.tryVisit!( (ref string s) { writeln("Why does this work? ", s); } ); writeln(test); } You should get: Why does this work? Test Test
Re: Trying to forward unwrapped opDispatch names to alias this
On Monday, 19 February 2018 at 08:28:22 UTC, aliak wrote: T is the wrapped type. So if T has a member (in the example it's the built in field "max") then forward that. Oh, I see what you mean. So the problem is that built in types don't have "members" per se, they have "magic". The built in properties don't count to `hasMember`. You could probably do `if(__traits(compiles, mixin("a." ~ member))` though. I assume this should work because rules for alias this (as I understand) are to basically try if there's a member name that resolves the call, else forward to alias this. yeah, just built in properties/members don't pass the same existence checks so that's confusing your template constraint.
Capturing by reference with "visit" from std.variant
I'm trying to understand why the following doesn't work: import std.stdio; import std.variant; void main() { Algebraic!(string, int) test = "Test"; test.tryVisit!( (ref string s) { s = "Why does this not work?"; } ); writeln(test); } But this works fine: *test.peek!string = "Works fine"; As far as I can tell, the "visit" template expands to something that ends up calling my handler like this: if (auto ptr = variant.peek!T) { handler(*ptr); } But seeing as the handler in my case takes a reference, shouldn't that work just fine? What am I missing?
Re: Manually allocating a File
On Tuesday, 20 February 2018 at 15:18:11 UTC, Adam D. Ruppe wrote: On Tuesday, 20 February 2018 at 14:56:54 UTC, Chris M. wrote: I'm doing this mainly for experimentation, but the following piece of code gives all sorts of errors. so important note: this will perform worse than the automatic allocation, which just puts it down in-place. You should basically never `new` or `malloc` a `File` struct. (in fact, i think we should @disable new on it! yes, D can do that!) auto f = cast(File *) malloc(File.sizeof); *f = File("test.txt", "r"); Two notes here: 1) it will call the destructor on *f, which is uninitialized data and 2) the destructor that actually wants to run will have to wait until the GC reaps it, which is also not ideal. The segfault you see is in #2 here, though I'm not sure exactly what is causing it, could be out-of-order, could be some other corruption, idk. Generally, when manually allocating D objects, first malloc it, then copy the `.init` value over, before casting it to the other type - do all this as bytes so it doesn't trigger postblits, destructors, etc like a real struct. The `std.conv.emplace` function from phobos does this and its source code can help you see how in a few cases. Thanks for the info, that clears things up. Like I said, it was more experimentation rather than me planning to actually use it. Works now with the following modifications. import std.stdio; import core.stdc.stdlib; import std.conv; void main() { auto f = cast(File*) malloc(File.sizeof); emplace!File(f, "test.txt", "r"); f.readln.write; free(f); } (*f).readln.writeln; That * is unnecessary btw, in D you can f.readln and it will see it is a pointer to struct and dereference it for you. That's what I had originally, but I put the * in to see if it would help with the original issue and never removed it
Re: Generic Property Implementation
On Tuesday, 20 February 2018 at 14:34:53 UTC, bauss wrote: Would there be a reason why this wouldn't be a good implementation? What is the intended use case for this? The main feature seems to be an ability to have read-only members, which is nice. Are there other benefits? If so what and how could it be improved? Are there flaws in an implementation like this? [snip] bool opEquals(T other) Why not use the default == implementation? It's supposed to do the right thing. Yours will fail for structs and classes. Since you're using alias this, it shouldn't even be necessary to implement opEquals - the right one should be called automagically. static if (!(isArray!T) && !(isAssociativeArray!T)) { int opCmp(T other) This will give unexpected errors when comparing structs or classes that don't overload opCmp. Better to replace the static if with is(typeof(T.init < T.init)), or better yet, std.traits.isOrderingComparable. Also, it's not necessary - alias this ensures that the correct opCmp function is called. string toString() Why not use std.typecons.to for everything in here? Its implementors probably have thought of many more corner cases than you have. There is an argument to be made for compile-time overhead, I guess. So, my version of the same: struct Property(T, bool readOnly = false) { private T _value; static if (readOnly) T __GET() { return _value; } else ref T __GET() { return _value; } alias __GET this; string toString() { import std.conv : to; return _value.to!string; } } One more thing: while you provided some very nice examples of how to use the type, it would be nice to have these more test-like. Something like this: unittest { auto foo = new Foo; foo.baz = "Hello"; // Check that value was set correctly: assert(foo.baz == "Hello"); // You can even check if something will compile: assert(!__traits(compiles, foo.bar = 24)); } Sorry if this came off a bit crass - I don't understand when I should use it, so it's hard to give feedback on that. -- Simen
Re: Manually allocating a File
On Tuesday, 20 February 2018 at 14:56:54 UTC, Chris M. wrote: I'm doing this mainly for experimentation, but the following piece of code gives all sorts of errors. Hangs, segfaults or prints nothing and exits import std.stdio; import core.stdc.stdlib; void main() { auto f = cast(File *) malloc(File.sizeof); *f = File("test.txt", "r"); (*f).readln.writeln; // freeing is for chumps } I could have sworn I've done something similar recently and it worked, unfortunately I can't remember what the case was. This is what gdb gave me on a segfault Program received signal SIGSEGV, Segmentation fault. 0x77a56c32 in _D2rt5minfo__T17runModuleFuncsRevSQBgQBg11ModuleGroup8runDtorsMFZ9__lambda1ZQCkMFAxPyS6object10ModuleInfoZv () from /usr/lib64/libphobos2.so.0.78 So it looks like it's reaching the end of main. Past that I can't tell what's going on. Ideas? File * is a C standard library type. You mixed the C standard library and D standard library in a way that would not work. The correct way to initialize a File*: void main() { import core.stdc.stdlib; auto F = fopen("test.txt", "r"); scope_exit(fclose(f)); //Reading from the file: char[100] str; fgets([0], s.length, f); } Honestly speaking though you should avoid using the C library when you can use th D standard library. You can read more on the C standard library and how to use it here: http://en.cppreference.com/w/c/io
Re: Manually allocating a File
On Tuesday, 20 February 2018 at 15:21:59 UTC, Uknown wrote: On Tuesday, 20 February 2018 at 14:56:54 UTC, Chris M. wrote: void main() [snip] Never mind, I confused FILE* with File...
Re: Manually allocating a File
On Tuesday, 20 February 2018 at 14:56:54 UTC, Chris M. wrote: I'm doing this mainly for experimentation, but the following piece of code gives all sorts of errors. so important note: this will perform worse than the automatic allocation, which just puts it down in-place. You should basically never `new` or `malloc` a `File` struct. (in fact, i think we should @disable new on it! yes, D can do that!) auto f = cast(File *) malloc(File.sizeof); *f = File("test.txt", "r"); Two notes here: 1) it will call the destructor on *f, which is uninitialized data and 2) the destructor that actually wants to run will have to wait until the GC reaps it, which is also not ideal. The segfault you see is in #2 here, though I'm not sure exactly what is causing it, could be out-of-order, could be some other corruption, idk. Generally, when manually allocating D objects, first malloc it, then copy the `.init` value over, before casting it to the other type - do all this as bytes so it doesn't trigger postblits, destructors, etc like a real struct. The `std.conv.emplace` function from phobos does this and its source code can help you see how in a few cases. (*f).readln.writeln; That * is unnecessary btw, in D you can f.readln and it will see it is a pointer to struct and dereference it for you. I could have sworn I've done something similar recently and it worked, unfortunately I can't remember what the case was. With other types, you can do this, but File is designed to be used in-place...
Manually allocating a File
I'm doing this mainly for experimentation, but the following piece of code gives all sorts of errors. Hangs, segfaults or prints nothing and exits import std.stdio; import core.stdc.stdlib; void main() { auto f = cast(File *) malloc(File.sizeof); *f = File("test.txt", "r"); (*f).readln.writeln; // freeing is for chumps } I could have sworn I've done something similar recently and it worked, unfortunately I can't remember what the case was. This is what gdb gave me on a segfault Program received signal SIGSEGV, Segmentation fault. 0x77a56c32 in _D2rt5minfo__T17runModuleFuncsRevSQBgQBg11ModuleGroup8runDtorsMFZ9__lambda1ZQCkMFAxPyS6object10ModuleInfoZv () from /usr/lib64/libphobos2.so.0.78 So it looks like it's reaching the end of main. Past that I can't tell what's going on. Ideas?
Re: Link to https://run.dlang.io/ ??
I recently tried to go to that site, and I tried `run.dlang.com` which is the wrong URL. So I was looking through the D homepage for the right link but couldn't find it. Even a Google search for "online d compiler" or "run dlang online" didn't turn up anything directly, I had to go through the "Online compilers" page on the D wiki to finally get the right URL. And I already knew it existed. Putting more links to it definitely seems like a good idea since it's such a useful tool.
Generic Property Implementation
Would there be a reason why this wouldn't be a good implementation? If so what and how could it be improved? Are there flaws in an implementation like this? struct Property(T, bool readOnly = false) { import std.traits : isScalarType, isArray, isAssociativeArray, isSomeString; private T _value; T __GET() { return _value; } static if (readOnly) { private { void __SET(T newValue) { _value = newValue; } void opAssign(T newValue) { __SET(newValue); } } } else { void __SET(T newValue) { _value = newValue; } void opAssign(T newValue) { __SET(newValue); } } bool opEquals(T other) { static if (isScalarType!T || isArray!T || isAssociativeArray!T) { return _value == other; } else { if (_value is other) return true; if (_value is null || other is null) return false; if (typeid(_value) == typeid(other)) return _value.opEquals(other); return _value.opEquals(other) && other.opEquals(_value); } } static if (!(isArray!T) && !(isAssociativeArray!T)) { int opCmp(T other) { static if (isScalarType!T) { if (_value < other) return -1; if (_value > other) return 1; return 0; } else { return _value.opCmp(other); } } } string toString() { static if (isArray!T && isSomeString!T) { return _value; } else static if (__traits(hasMember, T, "toString")) { return _value.toString(); } else { import std.conv : to; return to!string(_value); } } alias __GET this; } alias ReadOnlyProperty(T) = Property!(T, true); --- This would allow something like this: class Foo { ReadOnlyProperty!int bar; Property!string baz; Property!int faz; Property!Bar bar2; } class Bar { int boo; } Which can be used like: auto foo = new Foo; foo.baz = "Hello"; foo.baz = foo.baz ~ " World!"; foo.faz = 250 + foo.bar; foo.bar2 = new Bar; foo.bar2.boo = 200; import std.stdio; writeln(foo.bar); writeln(foo.baz); writeln(foo.faz); writeln(foo.bar2.boo);
Re: How to make AA key a pointer
On Monday, 19 February 2018 at 15:02:29 UTC, ketmar wrote: Clinton wrote: On Monday, 19 February 2018 at 14:55:01 UTC, Clinton wrote: [...] Sorry, on second look my explanation isn't very clear. I want to know if: bool[string] myAA; myAA[contact.id] = true; // Does this copy contact.id or is this a pointer to contact.id? there is absolutely no reason to copy `string` ever, as it is `immutable`. and compiler knows that. anyway, why don't you just check it by writing the code first? import std.stdio; void main () { int[string] a; string s = "test"; writefln("%08x", s.ptr); a[s] = 666; s = "test1"; writefln("%08x", s.ptr); a[s] = 42; foreach (string k; a.byKey) writefln("%08x", k.ptr); } Thanks. I actually did a similar test a little while ago and found it out. Thanks for confirming. I still struggle a bit with these basic things.
Link to https://run.dlang.io/ ??
It would be nice if there was a link to https://run.dlang.io/ on the dlang website - preferably under Resources drop-down menu or similar. Or is there somewhere and I am just missing it? I have bookmarked it for myself, but it would be better for newbies if it was also easily accessible from the website.
Re: Trying to forward unwrapped opDispatch names to alias this
On Monday, 19 February 2018 at 08:28:22 UTC, aliak wrote: On Monday, 19 February 2018 at 01:00:23 UTC, Adam D. Ruppe wrote: On Monday, 19 February 2018 at 00:42:05 UTC, aliak wrote: struct B(T) { T t; A a; alias a this; auto opDispatch(string name)() if (hasMember!(T, name)) { return mixin("t." ~ name); Did you perhaps mean `A` instead of `T` here? cuz in your code T is int, not the struct. I don't think I did :p T is the wrapped type. So if T has a member (in the example it's the built in field "max") then forward that. If member not there then I figured the alias this would be used. I.e. in the example b.p should call A.p. I assume this should work because rules for alias this (as I understand) are to basically try if there's a member name that resolves the call, else forward to alias this. There is a related ticket, https://issues.dlang.org/show_bug.cgi?id=6434 However, not exactly facing this question. It seems, that currently opDispatch replaces alias this... (?) Nevertheless, not only T is the wrapped type, but also A with the alias. What to do, if both members have a property with the same name? And why?
Re: countUntil to print all the index of a given string.
On Tuesday, February 20, 2018 08:44:37 aberba via Digitalmars-d-learn wrote: > On Sunday, 18 February 2018 at 15:23:14 UTC, Cym13 wrote: > > On Sunday, 18 February 2018 at 14:48:59 UTC, Cym13 wrote: > >> [...] > > > > Just thought of a much better/simpler solution for that last > > case that also doesn't force you to read all data (which might > > > > be impossible when dealing with infinite ranges): > > import std.range; > > import std.algorithm; > > > > a[] > > > > .enumerate // get tuples (index, > > > > value) > > > > .filter!(t => t[1] == "Test2") // keep only if value == > > > > "Test2" > > > > .map!(t => t[0]) // keep only the index > > > > part > > > > .writeln; > > > > Completely lazy. > > How does one detect an operation as lazy or not? Is the some > compile-time or runtime check for that? > > My guess is by referring to the docs function signature. You have to read the docs or read the source code, though in general, functions that return a range type that wraps the original range tend to be lazy, whereas if a function returns the original range type or an array, then it's clearly not lazy. - Jonathan M Davis
Re: countUntil to print all the index of a given string.
On Sunday, 18 February 2018 at 15:23:14 UTC, Cym13 wrote: On Sunday, 18 February 2018 at 14:48:59 UTC, Cym13 wrote: [...] Just thought of a much better/simpler solution for that last case that also doesn't force you to read all data (which might be impossible when dealing with infinite ranges): import std.range; import std.algorithm; a[] .enumerate // get tuples (index, value) .filter!(t => t[1] == "Test2") // keep only if value == "Test2" .map!(t => t[0]) // keep only the index part .writeln; Completely lazy. How does one detect an operation as lazy or not? Is the some compile-time or runtime check for that? My guess is by referring to the docs function signature.