Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2018-02

Scoped enums and inferred types

28 messages · 10 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Ryan Joseph

I just learned FPC has scoped enums, which is nice, but they lack what you could call “relative inference” based on the type being passed. In the example below the parameter “t” is TMyType but passing “A” doesn’t work. Doesn’t the compiler know the type and can infer “A” is relative to the scoped enum? I understand there’s a possibility for name conflicts but you could just use the full name in that case.

Is this a missing feature or do you need to enable it or something?

{$scopedenums on}
type
	TMyType = (A, B, C);
{$scopedenums off}
procedure DoThis (t: TMyType);
begin
end;
DoThis(A);

Regards,
Ryan Joseph

Reply by email

Michael Van Canneyt
to Ryan Joseph
Ryan Joseph wrote 14 lines
I just learned FPC has scoped enums, which is nice, but they lack what you could call “relative inference” based on the type being passed. In the example below the parameter “t” is TMyType but passing “A” doesn’t work. Doesn’t the compiler know the type and can infer “A” is relative to the scoped enum? I understand there’s a possibility for name conflicts but you could just use the full name in that case. Is this a missing feature or do you need to enable it or something? {$scopedenums on} type TMyType = (A, B, C); {$scopedenums off} procedure DoThis (t: TMyType); begin end; DoThis(A);

The whole point of scoped enums is to force you to write TMyType.A everywhere.
So this is "as designed". If you want to be able to write A, don't use scoped enums.

Michael.

Reply by email

Sven Barth
to Michael Van Canneyt

Am 21.02.2018 04:09 schrieb "Ryan Joseph" :

Is this a missing feature or do you need to enable it or something?

There is none.

The compiler does not know the type of the parameter, because overload selection occurs only after all parameters have been parsed. So during parsing the compiler would not know from which enum type to select the enum value. Not to mention unsolvable overloads if there would be two enums with the same value and the same name of the routine.

This also would have the possibility to lead to confusion for the user (e.g. same case as above or add a variable with the same name into the mix).

Regards,
Sven

Reply by email

Ryan Joseph
to Michael Van Canneyt: The whole point of scoped enums is to force you to write TMyType.A everywhere. So this is…
On Feb 21, 2018, at 1:54 PM, Michael Van Canneyt wrote: So this is "as designed". If you want to be able to write A, don't use scoped enums.

I got the idea from Swift (one of the things it does right) and it’s really nice to be able say "this = .value” instead of NSSomeLongNameOfSomethingValueKey. It only works with assignments but it really cleans up code. The reasons I suggested it was because some of my function calls were just getting stupid long and annoying to type plus difficult to remember (that’s important for programmers!) so the compiler could certainly help here.

Regards,
Ryan Joseph

Reply by email

Ryan Joseph
to Sven Barth: Is this a missing feature or do you need to enable it or something? There is none. The…
On Feb 21, 2018, at 2:01 PM, Sven Barth via fpc-pascal wrote: The compiler does not know the type of the parameter, because overload selection occurs only after all parameters have been parsed. So during parsing the compiler would not know from which enum type to select the enum value. Not to mention unsolvable overloads if there would be two enums with the same value and the same name of the routine.

So it’s a limitation of overloading? I mentioned I saw the idea from Swift (https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html) but I don’t know if they have overloaded functions. That’s too bad, it would be a nice feature. Thanks.

Regards,
Ryan Joseph

Reply by email

Michael Van Canneyt
to Ryan Joseph: I got the idea from Swift (one of the things it does right) and it’s really nice to be…
Ryan Joseph wrote 14 lines
> On Feb 21, 2018, at 1:54 PM, Michael Van Canneyt wrote: > > So this is "as designed". If you want to be able to write A, don't use > scoped enums. I got the idea from Swift (one of the things it does right) and it’s really nice to be able say "this = .value” instead of NSSomeLongNameOfSomethingValueKey. It only works with assignments but it really cleans up code. The reasons I suggested it was because some of my function calls were just getting stupid long and annoying to type plus difficult to remember (that’s important for programmers!) so the compiler could certainly help here.

The job of the compiler is to create correct code. Not to ensure you write readable code. Every feature that is introduced offers more scope for abuse (generics jump to mind).

Only you can make sure your code is readable.

I think often the cure is often worse than the problem. Simply don't use scoped enums if you want to type less.

Michael.

Reply by email

Ryan Joseph
to Michael Van Canneyt
On Feb 21, 2018, at 2:57 PM, Michael Van Canneyt wrote: The job of the compiler is to create correct code. Not to ensure you write readable code. Every feature that is introduced offers more scope for abuse (generics jump to mind).

I think you may change your mind if you had to type NSSomeLongNameOfSomethingValueKey all day instead of .Value. Remembering prefix names is a waste of my time if the compiler can do it for me. I wonder how many hours total in my life I’ve spent writing lists of constants with prefixes and then typing them back out in function calls.

Regards,
Ryan Joseph

Reply by email

Mattias Gaertner
to Ryan Joseph: I just learned FPC has scoped enums, which is nice, but they lack what you could call…
[...]Doesn’t the compiler know the type and can infer “A” is relative to the scoped enum? I understand there’s a possibility for name conflicts but you could just use the full name in that case.

I think this would be confusing:

{$scopedenums on}
type
	TMyType = (A, B, C);
{$scopedenums off}
var
  A: integer;
procedure DoThis (t: TMyType; i: integer);
begin
end;
DoThis(A,A);

Mattias

Reply by email

Ryan Joseph
to Mattias Gaertner
Show quoted text 16 lines
On Feb 21, 2018, at 3:45 PM, Mattias Gaertner wrote: I think this would be confusing: {$scopedenums on} type TMyType = (A, B, C); {$scopedenums off} var A: integer; procedure DoThis (t: TMyType; i: integer); begin end; DoThis(A,A);

yes it would be. :) Swift lets you opt out if you want so you can still do DoThis(TMyType.A, A) in that case but more specifically Swift keeps the . prefix so it would be DoThis(.A, A). Not always what you want but in some cases it’s really handy. In many API’s there’s a constant plague of long enum names all starting with the same prefix so shortening them to the logical suffix when possible just make sense.

Here’s something that’s all too common in code. There’s obviously some redundancy here we could optimize out. The only reason we keep the prefix is for name space integrity but logically in code what I’m really thinking about is the suffix so it makes sense that if the compiler could infer this it would save me time.

type
TTileSortingFlag = ( kTileSortingStatic,
kTileSortingRamp,
kTileSortingIgnore,
kTileSortingParticle,
kTileSortingFloor,
kTileSortingFlat,
kTileSortingContainer,
kTileSortingOnRamp,
kTileSortingBufferStatic,
kTileSortingBufferDynamic

				);

Regards,
Ryan Joseph

Reply by email

Mattias Gaertner
to Ryan Joseph
Show quoted text 13 lines
[...] type TTileSortingFlag = ( kTileSortingStatic, kTileSortingRamp, kTileSortingIgnore, kTileSortingParticle, kTileSortingFloor, kTileSortingFlat, kTileSortingContainer, kTileSortingOnRamp, kTileSortingBufferStatic, kTileSortingBufferDynamic );

I doubt that many would be using scopedenums for that.

Mattias

Reply by email

Ryan Joseph
to Mattias Gaertner
On Feb 21, 2018, at 4:09 PM, Mattias Gaertner wrote: I doubt that many would be using scopedenums for that.

why not? Scoped enums did clean that up a little but still lots of extra typing during assignments and parameters.

That compared to the previous is a still win though (less code is always a good thing).

{$scopedenums on}
type
TTileSortingFlag = ( Static,
Ramp,
Ignore,
Particle,
Floor,
Flat,
Container,
OnRamp,
BufferStatic,
BufferDynamic

				);

{$scopedenums off}

Regards,
Ryan Joseph

Reply by email

Michael Van Canneyt
to Ryan Joseph
Ryan Joseph wrote 24 lines
> On Feb 21, 2018, at 4:09 PM, Mattias Gaertner wrote: > > I doubt that many would be using scopedenums for that. why not? Scoped enums did clean that up a little but still lots of extra typing during assignments and parameters. That compared to the previous is a still win though (less code is always a good thing). {$scopedenums on} type TTileSortingFlag = ( Static, Ramp, Ignore, Particle, Floor, Flat, Container, OnRamp, BufferStatic, BufferDynamic ); {$scopedenums off}

And if you use

  type
        TTileSortingFlag = (    tsfStatic,
                                tsfRamp,
                                tsfIgnore,
                                tsfParticle,
  etc. );

You don't need scoped enums at all and the problem goes away.

You create a problem, and then you demand that the compiler introduces tricks to solve the problem you created. Where is the logic in that ?

Don't create the problem to begin with, and we can all spend our time on something more useful.

Michael.

Reply by email

Martok
to Mattias Gaertner: On Wed, 21 Feb 2018 15:58:12 +0700 I doubt that many would be using scopedenums for that.…
I doubt that many would be using scopedenums for that.

Isn't that what they're for?

{$SCOPEDENUMS ON}
type
TTileSortingFlag = (Static,
Ramp,
Ignore,
Particle,
Floor,
Flat,
Container,
OnRamp,
BufferStatic,
BufferDynamic

                      );

As a side note, Ryan: if you need to interoperate with other languages, using enums is dangerous. FPC has different enum storage semantics from most other compilers, and will happily drop you into "undefined" behaviour. Example from C# syntax:

   enum Day : byte {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

is _not_ translated as

   {$PACKENUM 1}
   Day = (Sat=1, Sun, Mon, Tue, Wed, Thu, Fri);

Instead, write

   {$PACKENUM 1}
   Day = (_low=low(Byte), Sat=1, Sun, Mon, Tue, Wed, Thu, Fri, _hi=high(Byte));

Regards,
Martok

Reply by email

Michael Van Canneyt
to Martok: Isn't that what they're for? {$SCOPEDENUMS ON} type BufferDynamic ); As a side note,…
Martok wrote 20 lines
> I doubt that many would be using scopedenums for that. Isn't that what they're for? {$SCOPEDENUMS ON} type TTileSortingFlag = (Static, Ramp, Ignore, Particle, Floor, Flat, Container, OnRamp, BufferStatic, BufferDynamic ); As a side note, Ryan: if you need to interoperate with other languages, using enums is dangerous. FPC has different enum storage semantics from most other compilers, and will happily drop you into "undefined" behaviour.

I was waiting for this reply from you. You didn't disappoint ;)

Michael.

Reply by email

Sven Barth
to Ryan Joseph: I think you may change your mind if you had to type NSSomeLongNameOfSomethingValueKey all…
"Ryan Joseph" wrote
On Feb 21, 2018, at 2:57 PM, Michael Van Canneyt
The job of the compiler is to create correct code. Not to ensure you

write readable code. Every feature that is introduced

offers more scope for abuse (generics jump to mind).

I think you may change your mind if you had to type NSSomeLongNameOfSomethingValueKey all day instead of .Value. Remembering prefix names is a waste of my time if the compiler can do it for me. I wonder how many hours total in my life I’ve spent writing lists of constants with prefixes and then typing them back out in function calls.

I simply rely on my IDE (Lazarus, Visual Studio) to complete long identifier names for me so that I don't have to. *shrugs*

Regards,
Sven

Reply by email

Michael Van Canneyt
to Sven Barth
Sven Barth wrote 20 lines
Am 21.02.2018 09:48 schrieb "Ryan Joseph" : > On Feb 21, 2018, at 2:57 PM, Michael Van Canneyt wrote: > > The job of the compiler is to create correct code. Not to ensure you write readable code. Every feature that is introduced > offers more scope for abuse (generics jump to mind). I think you may change your mind if you had to type NSSomeLongNameOfSomethingValueKey all day instead of .Value. Remembering prefix names is a waste of my time if the compiler can do it for me. I wonder how many hours total in my life I’ve spent writing lists of constants with prefixes and then typing them back out in function calls. I simply rely on my IDE (Lazarus, Visual Studio) to complete long identifier names for me so that I don't have to. *shrugs*

My point exactly. Not the job of the compiler.

Michael.

Reply by email

russeld
to Ryan Joseph: I got the idea from Swift (one of the things it does right) and it’s really nice to be…

Pascal allows type declarations at the procedure level, so you could use type aliases to reduce your typing and still have code completion:

procedure foo();
type
  SVK = NSSomeLongNameOfSomethingValueKey;
var
  a: SVK;
begin
  a := SVK.some_key_value;
end;

If you tried to use SVK elsewhere an error would be raised

Regards
Russ

Reply by email

Martok
to Michael Van Canneyt: I was waiting for this reply from you. You didn't disappoint ;) Michael.
Michael Van Canneyt wrote
I was waiting for this reply from you. You didn't disappoint ;)

Got an image to maintain ;-)

Took me long enough to come up with that actually fairly simple workaround, might as well save others the trouble.

That stuff is like "bad keming", I just can't un-see it. In related news: the example program for #32743 is undefined with more than 10 arguments in fpc and does, in fact, generate slightly wonky code that falls apart at 255 arguments.

Regards,
Martok

Reply by email

Ryan Joseph
to Sven Barth: write readable code. Every feature that is introduced I think you may change your mind if…
On Feb 21, 2018, at 5:16 PM, Sven Barth via fpc-pascal wrote: I simply rely on my IDE (Lazarus, Visual Studio) to complete long identifier names for me so that I don't have to. *shrugs*

Sure enough it’s not a pressing matter by any means but I thought it was worth asking about since I saw it in Swift and it made sense.

Adding compiler features is a slippery slope and I have no idea where to draw lines. Scoped enums aren’t necessary and in fact enums in general could be replaced with constants easily enough so that feature was probably debated at some point also.

Regards,
Ryan Joseph

Reply by email

Michael Van Canneyt
to Ryan Joseph
Ryan Joseph wrote 12 lines
> On Feb 21, 2018, at 5:16 PM, Sven Barth via fpc-pascal wrote: > > I simply rely on my IDE (Lazarus, Visual Studio) to complete long identifier names for me so that I don't have to. *shrugs* Sure enough it’s not a pressing matter by any means but I thought it was worth asking about since I saw it in Swift and it made sense. Adding compiler features is a slippery slope and I have no idea where to draw lines. Scoped enums aren’t necessary and in fact enums in general could be replaced with constants easily enough so that feature was probably debated at some point also.

You lose an important type safety if you replace them with constants.

Enums have their uses.

As long as you stick to conservative use and accept that the compiler manages them, they are a powerful and useful construct. Otherwise you're probably better off using constants.

Michael.

Reply by email

Marco van de Voort
to Ryan Joseph: Sure enough it’s not a pressing matter by any means but I thought it was worth asking…

In our previous episode, Ryan Joseph said:

Adding compiler features is a slippery slope and I have no idea where to draw lines. Scoped enums aren?t necessary and in fact enums in general could be replaced with constants easily enough so that feature was probably debated at some point also.

Having to decide a variable is scoped or not when it is declared is not the most ideal solution anyway. Logically you would decide that when creating the scope, iow when importing, like in Modula2 or (afaik also) Extended Pascal.

Reply by email

Ryan Joseph
to Marco van de Voort
Show quoted text 6 lines
On Feb 21, 2018, at 10:18 PM, Marco van de Voort wrote: Having to decide a variable is scoped or not when it is declared is not the most ideal solution anyway. Logically you would decide that when creating the scope, iow when importing, like in Modula2 or (afaik also) Extended Pascal.

How does that look?

Regards,
Ryan Joseph

Reply by email

Ryan Joseph
to Michael Van Canneyt: You lose an important type safety if you replace them with constants. Enums have their…
On Feb 21, 2018, at 10:13 PM, Michael Van Canneyt wrote: You lose an important type safety if you replace them with constants.

I like enums also but you can imagine when they were proposed the compiler teams probably compared them to constants and decided if they were worth it bloat or not (much like my suggestion and what Swift decided to do). Ultimately it’s just another thing the user can do themselves instead of compiler but if you follow that road too far you’re back to assembly (on the other end is Java/Swift super high level languages).

Btw did enums exist in the first Pascal specs? I honestly kind of forgot about them since I was doing desktop apps from C based API’s that used all constants.

Regards,
Ryan Joseph

Reply by email

Bernd Oppolzer
to Ryan Joseph
Ryan Joseph wrote 7 lines
> On Feb 21, 2018, at 10:13 PM, Michael Van Canneyt wrote: > > You lose an important type safety if you replace them with constants. I like enums also but you can imagine when they were proposed the compiler teams probably compared them to constants and decided if they were worth it bloat or not (much like my suggestion and what Swift decided to do). Ultimately it’s just another thing the user can do themselves instead of compiler but if you follow that road too far you’re back to assembly (on the other end is Java/Swift super high level languages). Btw did enums exist in the first Pascal specs? I honestly kind of forgot about them since I was doing desktop apps from C based API’s that used all constants.

the very first Pascal specifications of the 1970s had enums, but they were called "scalar types" at that time.

see page 10: http://bernd-oppolzer.de/PascalReport.pdf (page 18 of the PDF).

Kind regards

Bernd

Show quoted text 7 lines
Regards, Ryan Joseph _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Marco van de Voort
to Ryan Joseph: How does that look? Ryan Joseph

In our previous episode, Ryan Joseph said:

Show quoted text 6 lines
> Having to decide a variable is scoped or not when it is declared is not the > most ideal solution anyway. Logically you would decide that when creating > the scope, iow when importing, like in Modula2 or (afaik also) Extended > Pascal. How does that look?

I don't know the EP syntax by heart, but M2 had

IMPORT X;

and

FROM X IMPORT Y;
FROM X IMPORT QUALIFIED Y;

The first would import all identifiers of a module qualified (iow scoped), so you could only access them as X.Y

The second one imports Y for use unqualified (so just "Y"), and the third, as the keyword says, qualified again, but for a single identifier. The places where Y is used in the FROM..IMPORt can be lists.

There was no unqualified mass-IMPORT (which would be like our USES).

IIRC FROM..IMPORTing an enum also imported all its fields, but I'm not entirely sure, it has been 15+ years :-)

The system gave a lot of control, but as with all control that comes with a lot of micromanaging and administration. While the purist probably won't agree I missed an unqualified import (simples USES) dearly.

Still, it would be nice to have some similar control for special cases.

E.g. I can imagine very well that it would be nice to only import a handful of procs and types from unit windows and avoid polution, and scoping all enums, but descoping them with from import for special or heavy-usage cases.

Reply by email

Benito van der Zander
to Sven Barth: write readable code. Every feature that is introduced I think you may change your mind if…

Hi Fpc-pascal Users,

I simply rely on my IDE (Lazarus, Visual Studio)  to complete long identifier names for me so that I don't have to. *shrugs*

 but you still need to read the identifiers and when they are too long, that takes time, too

How does that look?

No idea how it looked, but it could look like this:

uses definingUnit.TTileSortingFlag;

-> you need to write TTileSortingFlag.Static

uses definingUnit.TTileSortingFlag.Static;

or

uses definingUnit.TTileSortingFlag.*;

-> you can just write Static

Alternatively:

with TTileSortingFlag do

to just write Static
Bye,
Benito

Reply by email