Constructors automatically mapped in Mapped Types?

Given this mapped type declaration (compiling on ECHOES):

Foo = public class mapped to System.Random
end;

I can then instantiate using any of the constructors supported by the System.Random class even though I have not mapped any constructors:

var f1 := new Foo;          // seeds with 'timer'
var f2 := new Foo(100);   // seeds explicitly with 100

But this then fails (does not compile):

var i := f1.Next(100) + 1;          // ERR: No such member

The mapped type appears to be endowed with all of the constructors of the type that is mapped to. However, the resulting instance has only the methods of a System.Object (since we’re on ECHOES in this case). It does not provide any of the methods of System.Random unless I then explicitly map them:

Foo = public class mapped to System.Random
  method Next(limit: Integer): Integer; mapped to Next(limit);
end;

The absence of instance methods makes sense. If all methods were automatically mapped this would “pollute” the interface of that class on each platform.

But it seem a little surprising then that the constructors are automatically mapped. I might expect the default/parameterless constructor to be auto-mapped although even that may be questionable given that the existence of parameterless constructor on all underlying mapped types does not necessarily imply that those constructors will result in the same initialised state of an instance. If that makes sense.

So my question is…

Is this behaviour w.r.t constructors on mapped types “by design” and something that can/should be relied on, or should we explicitly map constructors when mapping types as we seem required to do for any other methods ?

ASIDE: Also a minor bug I think. This does not compile:

Foo = public class mapped to System.Random;

Yields “semi-colon required” error and even an auto-fixit which adds another semi-colon. Hence the “empty” declaration with an “end;” in my example above. “Empty” mapped type declarations might not make sense but this error that results is a bit odd.

Indeed, that seems like a bug.

Thanks, logged as bugs://75109

I wonder if it would not also be possible to suppress the platform members that are present.

i.e on ECHOES my f1/f2 instances have a GetHashCode() method etc. Since this is a mapped type, having any platform specifics in there is likely to be a mistake, no ?

May not be easily achieved however.

While I’m making suggestions, how about a simple directive as a more concise (in fact, simply abbreviated) method for mapping constructors / methods where the mapped member has the same signature ?

Foo = public class mapped to Random
  constructor; mapped to constructor;
  constructor(seed: Integer); mapped to constructor(seed);
end;

Would become:

Foo = public class mapped to System.Random
  constructor; mapped;
  constructor(seed: Integer); mapped;
end;

This would obviously fail compilation if the member had no exactly corresponding member in the mapped type.

1 Like

Well. On .NET, every object has Hash, ToString, and co. It would be weird to be dealing with an object, descendent from Object (like all objects do), and not have access to the members from Object?

I see your point about it making the types different across the platforms WRT those members — but we still gotta respect the class hierarchy.

Good idea, I like it.

Thanks, logged as bugs://75110

Yep, an Object is an Object (or an NSObject an NSObject etc), no avoiding that and the methods have to be there and so are bound to be callable by the platform frameworks and app code, no avoiding that either.

But I wonder if it might not be possible to distinguish somehow between mapped and “intrinsic” members in the code completion offerings ? Ideally even emit a warning for code that calls such members if they are used ?

Not sure about that latter idea though. Warnings should be addressable - to indicate to the compiler that you know what you are doing (on those occasions that you do).

i.e. You won’t always want to avoid the platform call, just let the compiler know it’s OK.

Not sure how you would do that in this case. Unless… might it be possible for the compiler to distinguish between an implicit platform call and a typecast call ?

var s := f1.ToString;           // Warning: Platform call on mapped type
var s := Object(f1).ToString;   // No warning

Or maybe a hint instead of a warning. But you get the idea. :slight_smile:

Hmm, maybe yeah…

both ideas worth considering, yes.

adding a cast would make such a warning addressable, yeah. (Foo as System.Object).Hash).

That said, specifically for ToString, i wanna put more workminto actually making that work on all platforms, because dealing with description vs toString vs ToString becomes annoying, We already dded the [ToString] attribute to let you define a single method, cross-platform. We’ll need to add something similar for calling it (i’ll probably look at just adding a ToString extension method for NSObject, for Nougat…

Oh, actually we already have that. Perfect. extension method Foundation.NSObject.ToString: String;

So to get back to this as it seems I never saw the original. For backward compatibility, if a mapped types does NOT implement any constructors it inherits the ones from the original class, else if it defines any, it doesn’t. There’s no good reason for this left (There was when we wrote it) except for backward compatibility.

bugs://75109 got closed with status notfixable.

bugs://E17484 was closed as fixed.