Does using modules have any advantages?

For other topics related to the FreeBASIC project or its community.
BastetFurry
Posts: 255
Joined: Jan 05, 2006 0:56

Postby BastetFurry » Oct 23, 2009 14:54

marcov wrote:I think your example is a bit to terse. I don't get it. Could you expand the example?


Ok:
main.bas

Code: Select all

#include "cpu.bi"

cpu.reset

do
  cpu.execute
  cpu.showregisters
loop until cpu.stop = 1

cpu.bas

Code: Select all

type cputype
  stop as byte
  'Some registers be here
  declare sub reset
  declare sub execute
  declare sub showregisters
end type

sub cputype.reset
  'Resets the virtual CPU
end sub
sub cputype.execute
  'Executes one opcode
end sub
sub cputype.showregisters
  'Prints out current register content
end sub

This is not an actual project, just an example.
anonymous1337
Posts: 5494
Joined: Sep 12, 2005 20:06
Location: California

Postby anonymous1337 » Oct 23, 2009 14:54

My two cents - that there are three primary aspects to programming.

Philosophy - Structure/Standards/Models. Programming paradigm and object models (ex, does every object deserve its own module?)
Aesthetics - Code visuals. Programmers have freedom to move the variables, classes and modules around however we please, sometimes only to change how code looks.
Purpose - Implementation. The functional segment of our code.

Programmers are expected to make optimal use of both functional and aesthetic principles. To resolve potential conflicts between the two, I offer the following:

1. Implementation First. What makes code do work. It is often best to create new implementations separately of an existing code base.
2. Cleanup Second. Aesthetics. Implementing spaghetti code is not easy.
3. Integration Last. Large projects have a tendency to pick a single 'philosophy' for the majority of the project. This sets constraints which may make direct implementations difficult.

Feel liberated to add to, remove from, and re-arrange this process. Some people work best within predefined models. Others work best with generic pseudo-code. Hands down, the person who knows their code best is you - the programmer.
Last edited by anonymous1337 on Feb 28, 2010 16:27, edited 1 time in total.
marcov
Posts: 3082
Joined: Jun 16, 2005 9:45
Location: Eindhoven, NL
Contact:

Postby marcov » Oct 23, 2009 15:14

(your style of quoting is confusing. I'll assume it was meant for me)

rdc wrote:
Modules, as they relate to FB, are a hold over from QB where you had to split your program up to fit into 64k memory space. They are essentially separate programs that the compiler would overlay parts of into memory. This is a module, not what you are talking about. This is why FB has implemented module specific commands like Common.


Good, so it is an overlay with some extensions. (Fortran common block alike?)

What you are talking about are include files in relation to FB. In C/C++ include file are both header and code files.


In Pascal they are Unit which are simply scoped code files.


(nitpicking: in ISO pascal and Modula2 they are called "modules". In UCSD pascal and derivates(including Borland dialects) they are called units, because this one bases on an earlier draft of the standard)

Anyway there is more to it. See the list of features above. It mostly allows the compiler to grasp how a program is divided into modules. This also has consequences for quality of errorgeneration, and incremental- and autobuilding. The speed increase is an useful by-product of this, when combined with binary headers, allowing you to stream a symtree directly into memory.

Another major advantage from the isolation of modules is that it is easier to allow modules to be in distinct language dialects.

Macro's are a complication though, but partially that is also because Pascal doesn't even try too. One could try to fix this (but I would go the other way, first forbid macro's, then gradually add support again for safe cases)

The problem with that approach is that most "safe" cases are mostly better served by simply rewriting them to inline functions. But hygienic macros might be a more workable transitional step.

You can do the same thing with FB.


Can you walk my list of features above, and describe how FB deals with this? At least then there comes something good out of this discussion. And/or how the FB concept differs from C? (e.g. FB compilation units have scope? That doesn't make them modules, but at least that is further than C)

And I reuse code all the time. I structure it that way. Modules don't facilitate code reuse, good programming does despite what some ignorant a-hole may say (not you marcov) that talk a lot but produce zero code to show for it. Talk is cheap.


True modules makes it easier to do good programming. This affects both the number of programmers that can manage complex programs in the language, as well as the time experienced programmers have to expend to keep a large program managable.

Note that there are million line programs in Free Pascal, not counting headers.
Last edited by marcov on Apr 25, 2011 13:48, edited 3 times in total.
Galeon
Posts: 563
Joined: Apr 08, 2009 5:30
Location: Philippines
Contact:

Postby Galeon » Oct 24, 2009 10:50

In time, it is still useful in slow computers. According to relsoft, it took 2 minutes to compile fjrx in his own computer. It is divided in modules, but he compiles it all at once, the default in FbEdit.
1000101
Posts: 2556
Joined: Jun 13, 2005 23:14
Location: SK, Canada

Postby 1000101 » Oct 24, 2009 15:47

Is anyone really worried about compilation time? Seriously, if it takes less then 10 minutes then there is nothing to worry about. The idea behind binaries is you compile "once" (note, obviously you don't compile once but for large projects a make system will reduce compile times by not compiling unchanged modules). Even if it does take as long as an hour to compile, it's still a "do once" process. I really don't care if it takes an hour to compile someones program so long as it works and is worth the minor amount of time invested.

The bigger concern is code clarity. Since overlays have nothing to do with anything since 1993 I won't even mention them except to clarify that "module" is still accepted venacular for source code (source module as opposed to an overlay module). Clarity is important for anything which actually does take some time for compilation. Separate encapsulated modules are essential for this and impossible with includes. When you have a complex problem to solve it is most important that your solution be clear and understandable to others, not just yourself. This can not be done with include files and only leads to problems if they are required to be included in a specific order. Also, what if one include contains source depenant on another include? This is where modules come in, they are compiled and then linked together by the linker; that's it's job. Combined with a make system, even if the first compilation of the modules takes an hour assuming little changes, that time is reduces to minutes by not doing unneeded work since most of the modules didn't change and the object modules are still "current."


Includes are to define common elements between modules, not to contain code.
marcov
Posts: 3082
Joined: Jun 16, 2005 9:45
Location: Eindhoven, NL
Contact:

Postby marcov » Oct 24, 2009 16:27

1000101 wrote:Is anyone really worried about compilation time?


Yes.

Seriously, if it takes less then 10 minutes then there is nothing to worry about.


If an incremental build is more than one second, it is something to worry about. It disturbs the edit-compile-debug loop with unnecessary coffeebreaks. Most builds happen during development, and the final build that goes to end-users/customers is more the exception than the rule (and it indeed doesn't matter how long THAT takes)

I don't say this to be funny btw. It is really a target for embedded IDE/RADs like Lazarus.

Combined with a make system, even if the first compilation of the modules takes an hour assuming little changes, that time is reduces to minutes by not doing unneeded work since most of the modules didn't change and the object modules are still "current."


Requires manual maintenance of the make system, which is IMHO redundant. A make system can be useful to provide the build order of whole dirs, but micromanaging build order and dependancies of single files is IMHO a bridge to far.

The balkanisation of make systems doesn't really help either. Every vendor has its own. (and gnu make is only half usable on Windows)

Includes are to define common elements between modules, not to contain code.


Includes are just includes. Just like C has a lot of rules about includes (like e.g. the protection against being parsed twice), systems with stronger typing and module systems have their own rules.

E.g. Free Pascal has thousands of includes with code, the most important reason is platform dependant code (where the OS specific part is in an includefile per directory). The number of code-includes is very high in the lowlevel code of the RTL, and way rarer in higher level parts.

In app I work on at work, I don't have a few code-include. In all cases this is when the code is generated. (e.g. object types are generated from the database definitions, or large structured constants with built-in settings). THese are rare and isolated cases.

I think I don't have an include with types in it.

I do have several include files in nearly every files. They mostly contains batteries of conditionals (for testing with $ifdef), this is because preprocessor state of one module is isolated from another. Which means that global preprocessor settings aer usually in the form of an include file.
BigBaddie
Posts: 40
Joined: Oct 10, 2009 10:08
Location: Space
Contact:

Postby BigBaddie » Oct 24, 2009 18:35

Speed is important, 1000101. in ten minutes, a fast typer like me can
write a short mini-game. (i type 14 letters every two seconds), and
finnaly. modules are really REALLY good to use in a program. in every
10kb code i write. there are atleast 2 modules. but its the programmer's
choice of whether use it or not. the only thing that confuses me, is that
a question that can be answered by one. yes only one, good and
effective answer. has gotten more than 12 replies for a good nothing.
Eponasoft
Posts: 264
Joined: Jul 26, 2007 2:40

Postby Eponasoft » Oct 24, 2009 18:58

If it takes 10 minutes to compile your FB program, you're doing something horribly wrong. 10 minutes for something coded using gcc is not uncommon, but FB? Come on.
rdc
Posts: 1725
Joined: May 27, 2005 17:22
Location: Texas, USA
Contact:

Postby rdc » Oct 24, 2009 19:41

marcov wrote:I only know Wirthian module systems, but some of the things I like about them:


- faster compilation (no infinite reparsing). Note that precompiled headers (like in commercial C compilers) can provide this too, but at substantially more work, and without the other benefits.


I don't believe this is true of FB, unless you create and use libraries which are linked in.

- Compiler generates errors when compilation units don't match. compilation units don't meet only at the linker. The compiler has more information and an earlier opportunity to generate a decent error msg. (Undefined symbol "XX". Well ok, but why and where!??!)


FB is a single pass compiler so it will generate an error at the line. Whether it identifies the module I don't know.

- If you support nested scopes, the unit level (and importing in general) is just another one. The unit typically also provides a namespace. (*)


FB has namespace, Scope and End Scope as well as global, module and sub/func scope.

- Decentralized initialization and finalization code.


Yes in modules. Personally I haven't ever found this useful in FB or Pascal.

- The name mangling allows to have global variables in different modules with the same name. No endless renaming anymore if you piece code out of various origins together.


Namespaces cover this.

- the compiler has knowledge about what module are used. This means that the compiler can autobuild and -link without much additional info. This reduces the need for makefiles and/or additional building information (FPC only needs to know what the main program is, and can compile the program by just "compiler <mainprogram>", and figures out the rest itself)


Since FB is a single pass compiler you can't get these types of tricks as you can in a multi-pass compiler.

(*) Assume you have a module A and B, both exporting symbol X. If you import both into the main program, the order of importing will govern which X and Y are used. But you can always access individual implementations via A.X and B.X. Similarly for Y


Again, Namespace.

To make a very general comparison, FB modules are somewhat like Pascal units, and many of the same problems exist. You have to sometimes jumps through hoops in order to get different units/modules to work together. This is why C dropped the notion of units/modules, because they get can get in the way, rather than help.

In C++ instead of unit/modules, the class was added to make code reuse much easier, to get some of the benefits of units/modules, but without all the hassles. There are some hassles with classes, they are not the end-all solution, but they have proven much more useful than units/modules in the long run.

Now, the C++ model can be used in FB. FB supports objects and all the same features of C++ classes, except inheritance. So how does this enable code reuse?

Take a look at my Table Object. I happen to be using this in my DD2 project, and all I did was to:

Code: Select all

#Include "tableobj.bi"


Here is how I am using in DDD2:

Code: Select all

'Character attributes.
Type chattr
   Private:
   _attrmod As tableobj.tblobj
   _chstr As Integer  'strength
   _chend As Integer  'endurance
   _chdex As Integer  'dexterity
   _chagl As Integer  'agility
   _chint As Integer  'intelligence
   _chrstr As Double  'race strength modifier
   _chrend As Integer 'race endurance modifier
   _chrdex As Integer 'race dexterity modifier
   _chragl As Integer 'race agility modifier
   _chrint As Integer 'race intelligence modifier
   _maxhp As Integer  'max hit points
   _curhp As Integer  'current hp
   _maxpp As Integer  'max power
   _curpp As Integer  'current power
   _maxcap As Integer 'max capacity
   _curcap As Integer 'current capacity
   _chalc As Integer    'alchemy
   _chcrf As Integer    'crafting
   _chucf As Integer    'unarmed combat
   _chacf As Integer    'armed combat
   _chpcf As Integer    'projectile combat
   _chmcf As Integer    'magic combat
   _chcdf As Integer    'combat defense
   _chmdf As Integer    'magic defense
   Public:
   Declare Constructor (race As chraces)
   Declare Destructor ()
End Type

Constructor chattr (race As chraces)
   Dim row As Integer
   
   'Build the modifer table. Add a roiw for each attribute.
   'Will use the enum to index into the attr table.
   For i As Integer = chstr To chmdf
      row = _attrmod.AddRow()
   Next
End Constructor

Destructor chattr ()
   'Clear the modifier table.
   _attrmod.DestroyTable
End Destructor



chattr is a member of the player character object. _attrmod As tableobj.tblobj is the table object. I can do this because of the way I have structured the table object. Here is the basic outline:

Code: Select all

Namespace tableobj

const
enumerations

tcell object
tcell member code

table object
table member code

End Namespace


The namespace prevents naming collisions. I also wrap any #defines so that I don't have to worry about multiple defines.

Code: Select all

Create a NULL value.
#Ifndef NULL
    #Define NULL 0
#EndIf

'Create a True/False value.
#Ifndef FALSE
    #Define FALSE 0
    #Define TRUE (Not FALSE)
#EndIf


The other two objects I created for DD2, the Ini object and Stringlist follow the same format and are used in the same way. Instead of making these into a library, which would incrementally speed up compilation, I prefer to have source code reuse, just in case I need to tweak the object for a specific case. Since they are objects, they have constructors and destructors to make sure the object behaves correctly and the data is private to ensure data integrity and can only be accessed through the public interface.

This is how I do code reuse, and in my opinion, it is a much better solution to the problem.
1000101
Posts: 2556
Joined: Jun 13, 2005 23:14
Location: SK, Canada

Postby 1000101 » Oct 25, 2009 7:29

marcov, you make some interesting points on some of the things I said.

It seems that a few people noted the "10 minute compile times". The point about compile times being "10 minutes" wasn't that any large amount of time is acceptable but that in the case of machine-code compilers you should be compiling so infrequently in development that if it took as long as that it shouldn't matter that it took that long. I was also being more general (such as in the case of gcc code taking a long time to compile) and not about fbc specifically. My code does not take 10 minutes to compile in fbc and this is not where I got the idea. It was more that machine-code compilers have a very lose standard for x86 which it can generate code for in different ways depending on a few factors. I'm not saying that one doesn't have periods of frequent compiling but that reducing the number of times one does have to recompile should be a more critical issue.

As to a jit compiler, it seems that it would be fast, as most jit compilers are working within the better defined boundaries of the VM so you only have one code base instead of platform specific code.

I speak in general terms, please do not take my analogies to literally.
marcov
Posts: 3082
Joined: Jun 16, 2005 9:45
Location: Eindhoven, NL
Contact:

Postby marcov » Oct 25, 2009 9:40

1000101 wrote:marcov, you make some interesting points on some of the things I said.

It seems that a few people noted the "10 minute compile times". The point about compile times being "10 minutes" wasn't that any large amount of time is acceptable but that in the case of machine-code compilers you should be compiling so infrequently in development that if it took as long as that it shouldn't matter that it took that long.


I should nothing. See my IDE example, I routinely compile just ot do a syntax check. And the shorter the better.

Lazarus had a 10s delay until 2.2 when it got an internal linker, and it was a pain. (Delphi is near instantanious)

It was more that machine-code compilers have a very lose standard for x86 which it can generate code for in different ways depending on a few factors. I'm not saying that one doesn't have periods of frequent compiling but that reducing the number of times one does have to recompile should be a more critical issue.


First, even then shorter times are better. Moreover I don't see a fundamental difference here between JIT and native codegenerators.

Delphi is a native compiler, lighting fast, and is in the same ballpark performancewise as a C++ compiler. Sure, it might not do stuff like vectorisation, but the normal stuff is all there.

Reducing the number of times you have to compile is IMHO not related to the speed of each compilation.
marcov
Posts: 3082
Joined: Jun 16, 2005 9:45
Location: Eindhoven, NL
Contact:

Postby marcov » Oct 25, 2009 10:09

rdc wrote:
marcov wrote:I only know Wirthian module systems, but some of the things I like about them:


- faster compilation (no infinite reparsing). Note that precompiled headers (like in commercial C compilers) can provide this too, but at substantially more work, and without the other benefits.


I don't believe this is true of FB, unless you create and use libraries which are linked in.


If I include a .bi, is an ascii sourcefile parsed? Or is something binary from a previous run loaded from disk?

.bi's belong to the total source size, and if you compile multiple files, and .bi's are read multiple times, they count multiple times.

- Compiler generates errors when compilation units don't match. compilation units don't meet only at the linker. The compiler has more information and an earlier opportunity to generate a decent error msg. (Undefined symbol "XX". Well ok, but why and where!??!)


FB is a single pass compiler so it will generate an error at the line. Whether it identifies the module I don't know.


Will it generate an error when a .bas doesn't contain an symbol declared in the .bi ?

I've thought about this since yesterday, and this is more a strong bond between a .bi and the corresponding implementation (.bas), so that no missing symbol is considered external unless explicitely declared as such.

- If you support nested scopes, the unit level (and importing in general) is just another one. The unit typically also provides a namespace. (*)


FB has namespace, Scope and End Scope as well as global, module and sub/func scope.


Is there a document how these scopes work? Is there a scope stack?

- Decentralized initialization and finalization code.


Yes in modules. Personally I haven't ever found this useful in FB or Pascal.


It is mainly useful for plugin like architectures on source level. Having modules register them somewhere central (e.g. register class types, strings or whatever).

If you have a module managing hardware, the hardware initializations and finalizations might also go there.

- The name mangling allows to have global variables in different modules with the same name. No endless renaming anymore if you piece code out of various origins together.


Namespaces cover this.


I know FB has some. I don't know exactly how it works.

- the compiler has knowledge about what module are used. This means that the compiler can autobuild and -link without much additional info. This reduces the need for makefiles and/or additional building information (FPC only needs to know what the main program is, and can compile the program by just "compiler <mainprogram>", and figures out the rest itself)


Since FB is a single pass compiler you can't get these types of tricks as you can in a multi-pass compiler.


I'm not sure what you mean here. Single pass (like e.g. Pascal) means that a sourcecode is only passed once (which is faster but requires all symbols to be declared before used). Multi pass (like e.g. C) means that you first scan to find all identifiers and their rough type. (struct or int etc), and then compile during the second pass.

Afaik it hasn't any relevance on multi-module systems, since both terms have relevance on a single piece of source.

I have heard the term "single issue" once for a compiler that compiles only one sourcefile at a time (and then shuts down). Maybe you mean that?

To make a very general comparison, FB modules are somewhat like Pascal units, and many of the same problems exist. You have to sometimes jumps through hoops in order to get different units/modules to work together.

?

This is why C dropped the notion of units/modules, because they get can get in the way, rather than help.


C never had them. The design predates modules by 7 years. (module was the buzzword anno 1980)

To be honest, I don't think you really know what you are talking about.

In C++ instead of unit/modules, the class was added to make code reuse much easier, to get some of the benefits of units/modules, but without all the hassles. There are some hassles with classes, they are not the end-all solution, but they have proven much more useful than units/modules in the long run.


C++ is not something to orient against. Even Java or C# would be better. C++ had such horrible design constraints (because of C backwards compat) that it should be made an example for any language design.


A class is a stronger concept for code reuse, but, unless you put it central to the importing mechanism like e.g. Java does (with his classes hierarchy) doesn't provide a substitute for modules.

C++ does neither and has no concept of how files come together in a controlled way to form a program at all, and like C relies on interpreting linker errors if something is wrong. Classes don't help there in the C++ case.

IOW in Java/C#, a light module concept has been added to the class, in C++ not. I don't like the Java solution, but at least it is a solution to the problem. C++ has none to my best knowledge, and relies on out-of-language tools for this (makefiles, non-standarized project systems that usually map to makefiles or something similar). But in fact it is manual, with patchy tools to help with it, not a system.

So to be honest, I have the feeling you have no clue at all what this part of a module system is about, and know only C/C++.

Now, the C++ model can be used in FB. FB supports objects and all the same features of C++ classes, except inheritance. So how does this enable code reuse?

This is how I do code reuse, and in my opinion, it is a much better solution to the problem.


To me your example shows some of the problems, like the need to wrap. (why would you need to if you have namespaces?)

Where do you import the namespace into your local namespace, or does FB always require you to prefix tableobj. ?

Again: I really think you don't understand at all what I'm hinting at. If you want to talk about it, I'm on IRC. I do think you intuitively are on the right track, otherwise you wouldn't have named the single vs multi-issue bit. But somehow you miss some of it.
1000101
Posts: 2556
Joined: Jun 13, 2005 23:14
Location: SK, Canada

Postby 1000101 » Oct 25, 2009 17:10

marcov wrote:See my IDE example, I routinely compile just ot do a syntax check.


imo, if you just got an IDE that does syntax checking you have solved half the problem.


Anyway, we're getting into the realm of opinion where none us will agree. A lot of people are concerned with compile times where I am more concerned with functionality of the compiler. Not that I want huge compile times, I'm just less worried about it.
rdc
Posts: 1725
Joined: May 27, 2005 17:22
Location: Texas, USA
Contact:

Postby rdc » Oct 25, 2009 17:27

marcov wrote:To be honest, I don't think you really know what you are talking about.


Really? I suggest you study your computing history. Just because you have come up with your peculiar definition of what constitutes a module, doesn't change the fact that the idea goes back to the early days of computing.

The idea of modules goes back to the pseudo-code interpreters developed for early main frame machines to implement things like floating point operations on integer machines. The technique was extended to subroutines, and functions, and finally code modules. See Principles of Programming Languages by Bruce J. MacLennan.

Really, I see no reason to continue this discussion. If you want to program like it is the '80's then go ahead. There is nothing stopping you.
marcov
Posts: 3082
Joined: Jun 16, 2005 9:45
Location: Eindhoven, NL
Contact:

Postby marcov » Oct 25, 2009 20:12

The idea of modules goes back to the pseudo-code interpreters developed for early main frame machines to implement things like floating point operations on integer machines. The technique was extended to subroutines, and functions, and finally code modules. See Principles of Programming
Languages by Bruce J. MacLennan.


I'll stick to the various bits on modular programming by a certain N. Wirth.

Really, I see no reason to continue this discussion. If you want to program like it is the '80's then go ahead. There is nothing stopping you.


Well, at least it is an improvement on 60's and 70's manual combining of compilation units to programs, with a minimal token namespace addition.

Because that's what you are proposing, and you are not saying even that outright. It is what I have to distil myself from your comparisons with C++.

Modular programming is still relevant despite being a late seventies, early eighties hype. Just like OOP, while a late eighties, early nineties hype, is still relevant today.
Last edited by marcov on Jun 13, 2011 17:37, edited 1 time in total.

Return to “Community Discussion”

Who is online

Users browsing this forum: No registered users and 5 guests