* * *

Author Topic: Bounded With  (Read 1178 times)

AlexK

  • Jr. Member
  • **
  • Posts: 55
Bounded With
« on: July 09, 2017, 05:38:04 pm »
One thing that's missing in FPC is object protocols, I mean like with statement in Python.
The with statement already exists in FPC, but it's very simple, syntactic convenience.

Code: Python  [Select]
  1. # open returns a "context manager" object that implements methods __enter__ and __exit__, a protocol.
  2. with open("file.txt") as f:
  3.     data = f.read()
  4.     print(data)
  5.  

1) with statement calls context manager's __enter__ in order to init some context, if optional "as f" is present, it is assigned to what __enter__ returns.
2) Execute statements in a block
3) call CM's __exit__ to close context. If block raised exception, this method receives exception in its arguments.

Code: Pascal  [Select]
  1. // C# has a "Using" statement, but it merely calls a destructor of used object in the end.
  2. var TheCustomer: Customer;
  3.  
  4. Using TheCustomer to obj do  // "to obj" is optional
  5.     begin
  6.     // do something in a created context here
  7.     obj.SomeMethod; // obj is returned by __enter__
  8.     // compiler calls obj.Destroy here
  9.     end;
  10.  
  11. // Where:
  12. Customer = class     // implements "context manager" protocol for a Using statement
  13.     procedure __enter__;
  14.     function __enter__: Sometype; // returned value assigned "to obj" above
  15.     procedure __exit__;
  16.     procedure __rescue__(ex: Exception);
  17. end;
  18.  
« Last Edit: July 09, 2017, 05:51:10 pm by AlexK »

taazz

  • Hero Member
  • *****
  • Posts: 4269
Re: Bounded With
« Reply #1 on: July 09, 2017, 05:55:42 pm »
One thing that's missing in FPC is object protocols, I mean like with statement in Python.
The with statement already exists in FPC, but it's very simple, syntactic convenience.

Code: Python  [Select]
  1. # open returns a "context manager" object that implements methods __enter__ and __exit__, a protocol.
  2. with open("file.txt") as f:
  3.     data = f.read()
  4.     print(data)
  5.  

1) with statement calls context manager's __enter__ in order to init some context, if optional "as f" is present, it is assigned to what __enter__ returns.
2) Execute statements in a block
3) call CM's __exit__ to close context. If block raised exception, this method receives exception in its arguments.

Code: Pascal  [Select]
  1. // C# has a "Using" statement, but it only calls a destructor of used object in the end.
  2. var TheCustomer: Customer;
  3.  
  4. Using TheCustomer to obj do  // "to obj" is optional
  5.     begin
  6.     // do something in a created context here
  7.     obj.SomeMethod; // obj is returned by __enter__
  8.     // compiler calls obj.Destroy here
  9.     end;
  10.  
  11. // Given:
  12. Customer = class     // implements "context manager" protocol for a Using statement
  13.     procedure __enter__;
  14.     function __enter__: Sometype; // returned value assigned "to obj" above
  15.     procedure __exit__;
  16.     procedure __rescue__(ex: Exception);
  17. end;
  18.  
most of the time they are not needed in pascal eg
Code: Pascal  [Select]
  1. function Open(const aFilename:TFilename):TFileStream;
  2. begin
  3.    result := tfilestream.create(afilename,fmopenreadwriteorfmshareexclusive);
  4. end;
  5. procedure domytheng;
  6. begin
  7.   with open("MyDataFile.Dat") do
  8.   try
  9.     read(Data, size);
  10.   finally
  11.      free
  12.   end;
  13. end;
  14.  
but I agree it seems a bit more care free with out all that try/finally stuff ofcourse it shifts the responsibility for a proper destruction to the component writer instead it does not go away and I do not think that any component writer would like to take on more than it is absolutely necessary (my included). Look what happened with the build in for in support do you know many 3rd party controls that have build support?
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

AlexK

  • Jr. Member
  • **
  • Posts: 55
Re: Bounded With
« Reply #2 on: July 09, 2017, 09:39:49 pm »
most of the time they are not needed in pascal eg
Code: Pascal  [Select]
  1. function Open(const aFilename:TFilename):TFileStream;
  2. begin
  3.    result := tfilestream.create(afilename,fmopenreadwriteorfmshareexclusive);
  4. end;
  5. procedure domytheng;
  6. begin
  7.   with open("MyDataFile.Dat") do
  8.   try
  9.     read(Data, size);
  10.   finally
  11.      free
  12.   end;
  13. end;
  14.  
but I agree it seems a bit more care free with out all that try/finally stuff ofcourse it shifts the responsibility for a proper destruction to the component writer instead it does not go away and I do not think that any component writer would like to take on more than it is absolutely necessary (my included). Look what happened with the build in for in support do you know many 3rd party controls that have build support?

Imagine a complex system where you have to create or use some context before calling some of API functions.
You'll create similar try/finally blocks in different parts of your code. That'll take a lot of line, lower readability and maintainability.
What if you have 2 or more logically unrelated contexts that you have to create or use in one function? All they would go into your one try/finally block.

Code: Pascal  [Select]
  1. Using TheCustomer, SomeOtherObject to name1, OtherHelperContext to name2 do
  2.     begin
  3.     // here needed environment(context) is ensured by these "context managers" above
  4.     name1.someMethod;
  5.     name2.interestingMethod;
  6.     end;
  7.  

All objects in a using statement must implement intrinsic interface:

Code: Pascal  [Select]
  1. IContextManager = Interface
  2.   // the actual type determined at compilation in class implementation
  3.   // it can be implemented as a procedure in which case "to name1" suffix will not be allowed
  4.   intrinsic __enter__: DeferredType; // RETURNED VALUE ASSIGNED to "name1", "name2" above
  5.   procedure __exit__;
  6.   procedure __rescue__(ex: Exception); // called when "using" statement raises an exception
  7. end;
  8.  

So, that construction somehow partly replaces RAII of C++, gives some "design by contract" abilities.
Also it can be used to program transactions with rollbacks.
Or using draw procedures in some graphic contexts, or action procedures in a game.

To repeat:
1) Compiler inserts calls to __enter__ implementation when compiling "using" statement, before begin..end, so to speak.
__enter__ implementations can return type that are assigned to name in "using" statement clauses.
2) At the end compiler inserts calls to __exit__ procedures.

If exception occurs, it should call __resque__ procedures.
« Last Edit: July 09, 2017, 10:00:40 pm by AlexK »

bylaardt

  • Full Member
  • ***
  • Posts: 244
Re: Bounded With
« Reply #3 on: July 10, 2017, 04:36:27 am »
One thing that's missing in FPC is object protocols, I mean like with statement in Python.
The with statement already exists in FPC, but it's very simple, syntactic convenience.

Code: Python  [Select]
  1. # open returns a "context manager" object that implements methods __enter__ and __exit__, a protocol.
  2. with open("file.txt") as f:
  3.     data = f.read()
  4.     print(data)
  5.  


What's wrong with
Code: Pascal  [Select]
  1. f:=open("file.txt")
?

AlexK

  • Jr. Member
  • **
  • Posts: 55
Re: Bounded With
« Reply #4 on: July 10, 2017, 06:22:48 am »
What's wrong with
Code: Pascal  [Select]
  1. f:=open("file.txt")
?

Sorry, but you didn't bother to understand my point. It wasn't only about some techniques of opening a file.
I tend to think the task would require writing another compiler without backward compatibility.

For example, an interface that can declare only the name of a procedural type, without actual types:
Code: Pascal  [Select]
  1. IContextManager = Interface
  2.   deferred __enter__: FuncOrProcType;
  3.   procedure __exit__;
  4.   procedure __rescue__(ex: Exception);
  5. end;
  6.  

^^Here, the type of __enter__ would be determined by a compiler at an implementation site, in a class. Only the name is enforced by the interface, this opens an ability to create "object protocols".
__enter__ can be implemented as a function or procedure, with any arguments and return types.

Such interface leads to rich semantics that use those required methods in a compiler:
Code: Pascal  [Select]
  1. Using TheCustomer, SomeOtherObject to name1, OtherHelperContext to name2 do
  2.     begin
  3.     // here needed environment(context) is ensured by these "context managers" above
  4.     name1.someMethod;
  5.     name2.interestingMethod;
  6.     end;
  7.  

A compiler would insert calls to __enter__ methods for each object in "TheCustomer, SomeOtherObject to name1, OtherHelperContext to name2", where name1 and name2 would require that __enter__ is implemented as a function rather than a procedure.
On exit for a using statement compiler would insert calls to __exit__ methods.

More organized memory management.
Quote
So, that construction somehow partly replaces RAII of C++, gives some "design by contract" abilities.
Also it can be used to program transactions with rollbacks.
Or using draw procedures in some graphic contexts, or action procedures in a game.

AlexK

  • Jr. Member
  • **
  • Posts: 55
Re: Bounded With
« Reply #5 on: July 10, 2017, 08:25:28 am »
you have your init right there and when it fells off scope you have the destructor called automatically and it is as clean as your example
Keep in mind that I have not worked with object for a long long time so my facts might be upside down but the idea is there and I'm pretty sure can be made to work.
I do like the variable ability. In any case I'm all up for a new compiler but I'm warning you I'm not giving up my compatibility for any reason.

The programmer is responsible for calling the constructor and the destructor explicitly when using objects.

But it does not relate to what I meant. I took simple semantics in Python as an example and demonstrated how it could possibly be implemented in Object Pascal or some static typed lang.
« Last Edit: July 10, 2017, 08:27:38 am by AlexK »

Thaddy

  • Hero Member
  • *****
  • Posts: 4439
Re: Bounded With
« Reply #6 on: July 10, 2017, 12:19:53 pm »

I don't see it either. The challenges of programming nowadays are not due to a lack of language syntax micro optimization.  This is syntax navel-gazing, or syntax graffiti; people feel a need to mark a language as their own by adding something. And if you search long enough you'll find always some construct that can be a few chars shorter. But it makes the language as a whole more baroque and thus weaker.
Although I broadly agree with you, there are some newer features in the language that have merits in extending the language by helping defensive programming (which I always try to do).
A prime example being for.. in.. do, which protects a programmer from accidentally having a range wrong. I think such a feature would not make a language necessarily more baroque.
OTOH, there are some traditional features that should imo never have been there, like allowing with statements nestable. {$modeswitch nowith}

In general I support language features (implemented already  or not) that assist type safety, range and, to a certain extend, also memory management. 
« Last Edit: July 10, 2017, 12:27:08 pm by Thaddy »
"Logically, no number of positive outcomes at the level of experimental testing can confirm a scientific theory, but a single counterexample is logically decisive."

carl_caulkett

  • Full Member
  • ***
  • Posts: 243
Re: Bounded With
« Reply #7 on: July 12, 2017, 06:39:38 am »
On the subject of with statements, due to debugging related bugs in early versions of Delphi, I learnt never to use the with statement under any circumstance, nestable or not.

That is so ingrained in me now that I suspect I would never use them even if they are fixed. But out of interest, do they operate in a bug free manner in Lazarus/FPC nowadays?
macOS 10.12.6
Lazarus 1.8.0 RC4
FPC 3.0.2

taazz

  • Hero Member
  • *****
  • Posts: 4269
Re: Bounded With
« Reply #8 on: July 12, 2017, 07:13:36 am »
On the subject of with statements, due to debugging related bugs in early versions of Delphi, I learnt never to use the with statement under any circumstance, nestable or not.

That is so ingrained in me now that I suspect I would never use them even if they are fixed. But out of interest, do they operate in a bug free manner in Lazarus/FPC nowadays?
debugging bugs? what debugging bugs? The problem with the "with" statement, as far as I understand it, was one of scope, if it was ever bug it was in our ability to comprehend not the with statement.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Thaddy

  • Hero Member
  • *****
  • Posts: 4439
Re: Bounded With
« Reply #9 on: July 12, 2017, 08:32:21 am »
Yes, the problem with with is one big bug in programmers: when nested they do not keep track of scope. That's basically all.
In e.g. Python this is less of a problem, because the scope is immediately clear.
"Logically, no number of positive outcomes at the level of experimental testing can confirm a scientific theory, but a single counterexample is logically decisive."

Handoko

  • Hero Member
  • *****
  • Posts: 1514
  • My goal: build my own game engine using Lazarus
Re: Bounded With
« Reply #10 on: July 12, 2017, 09:03:10 am »
I use 'with' a lot, so far never had any problem.

Thaddy

  • Hero Member
  • *****
  • Posts: 4439
Re: Bounded With
« Reply #11 on: July 12, 2017, 09:14:53 am »
That's because you have a little more talent than others... Try to use it in a team: all hell breaks loose... :P
{This is also a case where my new signature applies..}
« Last Edit: July 12, 2017, 09:17:28 am by Thaddy »
"Logically, no number of positive outcomes at the level of experimental testing can confirm a scientific theory, but a single counterexample is logically decisive."

Blestan

  • Sr. Member
  • ****
  • Posts: 452
Re: Bounded With
« Reply #12 on: July 12, 2017, 09:41:12 am »
@thaddy:
Quote
{This is also a case where my new signature applies..}
what happened to the danish language?  O:-)

anyway even if "with" is just a shortcut  and its  a legacy from the dark ages before compiler optimizations and was used to tell the compiler to keep a pointer to something in a register is ok touse it when you know what your are doing
Speak postscript or die!
Translate to pdf and live!

AlexK

  • Jr. Member
  • **
  • Posts: 55
Re: Bounded With
« Reply #13 on: July 12, 2017, 10:00:15 am »
Yes, the problem with with is one big bug in programmers: when nested they do not keep track of scope. That's basically all.
In e.g. Python this is less of a problem, because the scope is immediately clear.

I realized that you(and others) are probably not aware that a WITH in Python and  FreePascal have completely different semantics.
In Object Pascal WITH is just a syntactic "sugar", and it's controversial, whether it increases readability or on the contrary.

In Python WITH is a syntax for Context Managers(an interface), it does not allow omission of instance names.
That's what I demoed as a possible implementation in Object Pascal, but called USING statement.
A more fundamental construct.

I'm still evaluating FreePascal(a couple of years), but it's not a short way because I come from a GNU/Linux environment, and use Emacs exclusively.
« Last Edit: July 12, 2017, 10:02:58 am by AlexK »

Thaddy

  • Hero Member
  • *****
  • Posts: 4439
Re: Bounded With
« Reply #14 on: July 12, 2017, 10:06:47 am »
Yes, the problem with with is one big bug in programmers: when nested they do not keep track of scope. That's basically all.
In e.g. Python this is less of a problem, because the scope is immediately clear.

I realized that you(and others) are probably not aware that a WITH in Python and  FreePascal have completely different semantics.
In Object Pascal WITH is just a syntactic "sugar", and it's controversial, whether it increases readability or on the contrary.

In Python WITH is a syntax for Context Managers(an interface), it does not allow omission of instance names.
That's what I demoed as a possible implementation in Object Pascal, but called USING statement.
A more fundamental construct.

I'm still evaluating FreePascal(a couple of years), but it's not a short way because I come from a GNU/Linux environment, and use Emacs exclusively.

Uhhmmmmm, NO. It has the exact same design purpose. Only in Python it is easier to spot scoping. Because it is Python. Indentation, silly.... <grumpy >:D>
Pascal does not know indentation at all. It knows ";". In Python indentation is part of the language. The most important part.
« Last Edit: July 12, 2017, 10:14:27 am by Thaddy »
"Logically, no number of positive outcomes at the level of experimental testing can confirm a scientific theory, but a single counterexample is logically decisive."

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus