Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.forth > #50166 > unrolled thread
| Started by | hughaguilar96@gmail.com |
|---|---|
| First post | 2016-08-15 23:10 -0700 |
| Last post | 2016-08-18 18:31 -0700 |
| Articles | 5 on this page of 45 — 12 participants |
Back to article view | Back to comp.lang.forth
the IS-A function hughaguilar96@gmail.com - 2016-08-15 23:10 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-16 00:22 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-16 14:01 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-16 14:20 -0700
Re: the IS-A function JennyB <jennybrien@googlemail.com> - 2016-08-17 05:04 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-17 10:52 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-17 19:31 -0700
Re: the IS-A function anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-08-18 08:00 +0000
Re: the IS-A function JennyB <jennybrien@googlemail.com> - 2016-08-18 21:52 -0700
Re: the IS-A function Julian Fondren <julian.fondren@gmail.com> - 2016-08-17 07:11 -0700
Re: the IS-A function franck.bensusan@gmail.com - 2016-08-17 23:13 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-18 11:59 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-18 15:11 -0700
Re: the IS-A function franck.bensusan@gmail.com - 2016-08-18 23:14 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-19 01:01 -0700
Re: the IS-A function franck.bensusan@gmail.com - 2016-08-19 03:14 -0700
Re: the IS-A function Ron Aaron <rambamist@gmail.com> - 2016-08-19 13:31 +0300
Re: the IS-A function Raimond Dragomir <raimond.dragomir@gmail.com> - 2016-08-19 03:39 -0700
Re: the IS-A function franck.bensusan@gmail.com - 2016-08-19 06:49 -0700
Re: the IS-A function lawrencedo99@gmail.com - 2016-08-19 14:55 -0700
Re: the IS-A function Paul Rubin <no.email@nospam.invalid> - 2016-08-19 14:57 -0700
Re: the IS-A function franck.bensusan@gmail.com - 2016-08-20 10:29 -0700
Re: the IS-A function Ron Aaron <rambamist@gmail.com> - 2016-08-20 20:35 +0300
Re: the IS-A function Paul Rubin <no.email@nospam.invalid> - 2016-08-20 10:53 -0700
Re: the IS-A function Julian Fondren <julian.fondren@gmail.com> - 2016-08-20 11:01 -0700
Re: the IS-A function Paul Rubin <no.email@nospam.invalid> - 2016-08-20 19:36 -0700
Re: the IS-A function Julian Fondren <julian.fondren@gmail.com> - 2016-08-20 20:15 -0700
Re: the IS-A function lawrencedo99@gmail.com - 2016-08-21 14:53 -0700
Re: the IS-A function Cecil Bayona <cbayona@cbayona.com> - 2016-08-19 08:28 -0500
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-20 19:30 -0700
Re: the IS-A function Cecil Bayona <cbayona@cbayona.com> - 2016-08-20 22:20 -0500
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-20 20:50 -0700
OOP (was: the IS-A function) Ron Aaron <rambamist@gmail.com> - 2016-08-21 07:21 +0300
Re: the IS-A function Mark Wills <markwills1970@gmail.com> - 2016-08-21 01:42 -0700
Re: the IS-A function Coos Haak <htrofhc@gmail.com> - 2016-08-21 14:16 +0200
Re: the IS-A function Mark Wills <markwills1970@gmail.com> - 2016-08-21 11:08 -0700
Re: the IS-A function Coos Haak <htrofhc@gmail.com> - 2016-08-21 21:25 +0200
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-21 12:01 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-18 17:08 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-19 00:29 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-28 11:01 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-28 14:45 -0700
Re: the IS-A function Mark Wills <markwills1970@gmail.com> - 2016-08-18 02:22 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-18 12:02 -0700
Re: the IS-A function hughaguilar96@gmail.com - 2016-08-18 18:31 -0700
Page 3 of 3 — ← Prev page 1 2 [3]
| From | hughaguilar96@gmail.com |
|---|---|
| Date | 2016-08-28 11:01 -0700 |
| Message-ID | <e8482341-540d-4d40-93b1-4d430deeaa92@googlegroups.com> |
| In reply to | #50237 |
On Friday, August 19, 2016 at 12:29:25 AM UTC-7, hughag...@gmail.com wrote:
> On Thursday, August 18, 2016 at 5:08:34 PM UTC-7, hughag...@gmail.com wrote:
> > On Wednesday, August 17, 2016 at 11:13:30 PM UTC-7, franck....@gmail.com wrote:
> > > btw, what you call is-a is generally called IS-KINDOF (isKindOf in Oforth).
> > >
> > > is-a generally checks only if the object's class is the same but
> > > does not check its parent classes.
> >
> > Here is an updated version that changes IS-A to mean exactly that class, but now provides IS-KIND-OF to mean that class or any of its parents.
>
> There was a bug in this code. I was doing IS-KIND-OF backwards. I have changed some names to make it more understandable (I was confused by the names previously; hence the bug).
>
> I have a new version with these new features:
> 1.) IS-A and [IS-A] check if an object is a member of class.
> 2.) IS-CHILD-OF and [IS-CHILD-OF] check if an object has this class as an ancestor (may be more than one generation away).
> 3.) IS-PARENT-OF and [IS-PARENT-OF] check if an object has this class as a child (may be more than one generation away).
> 4.) BECOME and [BECOME] change an object into a new class (change its size and change its pointer to the new class). The new class must be either a parent or a child of the current class (may be more than one generation away) --- first use IS-CHILD-OF or IS-PARENT-OF respectively to make sure.
>
> IS-CHILD-OF and IS-PARENT-OF are useful to check if BECOME can be used (don't use BECOME without checking lest you get a catastrophe). IS-CHILD-OF is also useful to determine of a parent's methods can be used on your object.
>
> IS-A is mostly useful in situations like the hottie-counting program where we have a data-structure with data of several classes (presumably all in the same inheritance chain) and we want to handle them differently.
>
> Here is the new bug-free version:
>
>
>
> 98769876 constant class-id \ used to make sure a class is actually a class; must be a distinctive value
>
> 0
> w field .class-parent \ pointer to parent class \ must be first field
> w field .class-size \ size of object
> w field .class-id \ should always be CLASS-ID
> constant class-struct
>
> \ Every object has a W field in front which is a pointer to the class-struct for its class.
>
> : check-class ( class -- )
> .class-id @ class-id <> abort" *** tried to use a class that wasn't actually a class ***" ;
>
> : create-nada ( -- ) \ create a class that has no parent
> create
> nil , \ .class-parent \ there is no parent
> w , \ .class-size \ room for the object's pointer to its class-struct
> class-id , \ .class-id \ this is for error-checking
> does> ( -- )
> true abort" *** it is illegal to make objects of class NADA ***" ;
>
> create-nada nada \ NADA is the only class that has no parent
>
> \ NADA is only available so it can be the parent of classes that have no parent.
> \ It is not really a class that can be used as such.
>
> ' alloc constant heap-new \ give this to the class to get a new object in the heap
> ' concrete-alloc constant dict-new \ give this to the class to get a new object in the dictionary
>
> : class-size ( class -- size )
> .class-size @
> dup 0< abort" *** CLASS-SIZE given a class that never had its .CLASS-SIZE field set by END-CLASS ***" ;
>
> : object-size ( object -- size )
> @ \ -- class
> .class-size @ ; \ -- size
>
> : class ( -- class offset ) \ stream: class-name parent-name
> create here \ -- class
> ' >body \ -- class parent
> dup check-class \ -- class parent \ make sure PARENT is a class
> dup , \ -- class parent \ .class-parent
> -1 , \ -- class parent \ .class-size \ this will get filled in by END-CLASS
> class-id , \ -- class parent \ .class-id \ this is for error-checking
> class-size \ -- class offset
> does> ( 'alloc -- adr ) \ make a new object ( 'ALLOC is either HEAP-NEW or DICT-NEW)
> >r \ 'alloc -- \ r: -- class
> r@ class-size swap execute \ -- object
> dup r@ class-size erase \ zero out the object
> r> over ! ; \ first field of object is pointer to class
>
> : end-class ( class offset -- )
> over check-class \ make sure CLASS is a class
> swap .class-size ! ;
>
> : <<is-a>> ( object target -- flag )
> over check-class over check-class
> = ;
>
> : <is-a> ( object 'target -- flag )
> swap @ swap >body <<is-a>> ;
>
> : is-a ( object -- flag ) \ stream: target-class-name
> ' <is-a> ;
>
> : [is-a] ( object -- flag ) \ stream: target-class-name
> postpone ['] postpone <is-a> ;
> immediate
>
> : <<is-child-of>> ( ancestor class -- flag )
> over check-class over check-class
> begin
> 2dup = if 2drop true exit then
> @ \ -- ancestor parent \ .class-parent is first field
> dup 0= if 2drop false exit then
> again ;
>
> : <is-child-of> ( object 'ancestor )
> >body swap @ <<is-child-of>> ;
>
> : is-child-of ( object -- flag ) \ stream: ancestor-class-name
> ' <is-child-of> ;
>
> : [is-child-of] ( object -- flag ) \ stream: ancestor-class-name
> postpone ['] postpone <is-child-of> ;
> immediate
>
> \ IS-CHILD-OF tests if the object has ANCESTOR as an ancestor, meaning that it can use ANCESTOR's methods.
>
> : <is-parent-of> ( object 'child )
> >body swap @ swap <<is-child-of>> ;
>
> : is-parent-of ( object -- flag ) \ stream: child-class-name
> ' <is-parent-of> ;
>
> : [is-parent-of] ( object -- flag ) \ stream: child-class-name
> postpone ['] postpone <is-parent-of> ;
> immediate
>
> \ IS-PARENT-OF tests if the object has CHILD as a child, meaning that it can be upgraded to be a CHILD.
>
> : <become> ( object 'class -- new-object )
> >body >r \ -- object \ r: -- class
> r@ class-size realloc \ -- new-object
> r> over ! ; \ -- new-object \ class is object's first field
>
> \ <BECOME> converts an object into a new class.
> \ The new class must either be an ancestor or a child of the object's class.
> \ Use IS-CHILD-OF to find out if the object is a child of the new class (can be downgraded to the parent class).
> \ Use IS-PARENT-OF to find out if the object is a parent of the new-class (can be upgraded to the child class).
>
> : become ( object -- new-object ) \ stream: class-name
> ' <become> ;
>
> : [become] ( object -- new-object ) \ stream: class-name
> postpone ['] postpone <become> ;
> immediate
Well, I read Bernd Paysan's article on Mini-OOF:
https://bernd-paysan.de/mini-oof.html
His METHOD is somewhat cute, so I implemented it, but then I didn't like it and discarded it. The only way that I could implement it was with CREATE DOES>, and consequently it was grossly inefficient, so I got rid of it because that is not my style at all (grossly inefficient code is Bernd's style).
The following is example code for a simple class:
class slnk nada
w field slnk.fore
end-class
: slnk.init ( node -- node )
nil over slnk.fore ! ;
: <slnk.kill> ( node -- )
drop ;
: slnk.each ( head rq -- ) \ rq: i*x node -- j*x
{ rq | next -- }
begin ?dup while \ -- node
dup slnk.fore @ to next
rq rex
next repeat ;
: slnk.tail ( head -- node )
nil swap begin dup while \ -- prev node
nip dup slnk.fore @ repeat drop ;
: slnk.link ( 1stHead 2ndHead -- NewHead ) \ makes one list
over 0= if nip exit then
dup 0= if drop exit then
over slnk.tail slnk.fore ! ;
This doesn't have all the features of LIST.4TH but I only provided a few methods. I wrote this just for demonstration purposes. This shows how CLASS is used, and also shows the naming convention that I am proposing for use with CLASS. Every field or method is the class name, then a dot, then the field or method name. An example of a field name is SLNK.FORE and examples of method names are SLNK.EACH SLNK.LINK etc.. Normally we would have a constructor and destructor, called SLNK.NEW and SLNK.KILL respectively. I didn't bother to write these because nobody will ever define an object of type SLNK --- who would want an object without any payload? --- SLNK is only intended to be used as the parent of classes that have a payload.
Here is an example of a class that inherits from the SLNK class:
class text slnk
w field text.adr
w field text.cnt
w field 'text.show
end-class
: text.show ( node -- )
dup 'text.show perform ;
: <text.show> ( node -- )
>r
cr r@ text.adr @ r@ text.cnt @ type
rdrop ;
: text.init ( adr cnt node -- node ) \ if adr:cnt came from $> then it is on the heap already
slnk.init >r
r@ text.cnt !
r@ text.adr !
['] <text.show> r@ 'text.show !
r> ;
: text.new ( adr cnt -- node )
heap-new text
text.init ;
: <text.kill> ( node -- ) \ assumes that the string was made with ALLOC or CONCRETE-ALLOC
dup <slnk.kill>
dup text.adr @ dealloc
drop ;
: text.kill ( node -- )
dup <text.kill>
dealloc ;
: text.show-all ( head -- )
['] text.show slnk.each ;
This class has a string as a payload. I now have a constructor TEXT.NEW that is used to construct a new object; it takes a string as a parameter and constructs the new object with that string as the payload (it assumes that the string came from ALLOC or CONCRETE-ALLOC). The convention here is that we have an initializer TEXT.INIT that does the actual construction, but we have the constructor TEXT.NEW that allocates the object and then calls TEXT.INIT for it. The reason why TEXT.INIT is factored out is so that it can be used in the constructor of any child class of TEXT --- this would be similar to how SLNK.INIT was used in the constructor for TEXT which is a child class of SLNK.
We also have a deconstructor TEXT.KILL that kills the object. This has <TEXT.KILL> factored out. The reason once again, is so <TEXT.KILL> can be used in the deconstructor of any child class of TEXT --- this would be similar to how <SLNK.KILL> was used in the deconstructor for TEXT which is a child class of SLNK.
Our TEXT class has a virtual method called TEXT.SHOW --- the xt is held in the field 'TEXT.SHOW --- the virtual method TEXT.SHOW executes whatever is in that field. It is the constructor's job to fill the 'TEXT.SHOW field with an xt --- in this case, the xt of <TEXT.SHOW> is used.
I don't have a VMT --- I am just using fields in the object to hold the xt values of the virtual methods --- what I'm doing is similar to what was done in the Oberon language.
The downside to my technique is that the objects are bigger because every object has all the fields needed for all of the virtual methods. I'm not worried about that given the size of RAM these days. Another downside is that the constructor is bigger and hence slower because it has to fill all those virtual-method fields with xt values. I'm not worried about that either because the inefficiency is not significant. The upside to my technique is that calling the virtual functions is very efficient --- if you look at the disassembly of my code compared to Bernd Paysan's code, you will find that it is significantly faster, partly because there is one less indirection (not going through a VMT) and mostly because I don't use CREATE DOES> which Bernd is using and which is always inefficient. My code is like an order of magnitude faster than Mini-OOF on SwiftForth because SwiftForth does CREATE DOES> in a grossly inefficient way. I don't have as great an advantage over Mini-OOF in VFX because VFX does CREATE DOES> more efficiently, but I'm still several times faster.
All in all, I think my OOP package is superior to Mini-OOF. These are both OOP packages that don't provide polymorphism, so they are in the same league. I have a lot more features though, and my virtual methods are a lot more efficient. The big advantage of my OOP package however, is that it is super-simple and easy to understand.
Both my OOP package and Mini-OOF are superior to SWOOP which is typical crappy Forth Inc. code without any practical value whatsoever --- SWOOP has polymorphism, which presumably was provided because it is cute --- SWOOP is like two orders of magnitude slower than my code though, and hence is not practical (it could only be used in cute little toy programs that have no speed requirements at all).
I may yet write that book that Cecil suggested: "ANS-Forth --- a Guide to Alternate Realities." If I do, I will use this OOP package as the basis for it. Right now, Forth Inc. is selling SWOOP which just makes the entire Forth community appear to be retarded --- my code is much better --- this might help to make the Forth community appear to be more aware of the concept of efficiency.
[toc] | [prev] | [next] | [standalone]
| From | hughaguilar96@gmail.com |
|---|---|
| Date | 2016-08-28 14:45 -0700 |
| Message-ID | <431c7beb-ab24-4a30-bbf4-5af275a38734@googlegroups.com> |
| In reply to | #50515 |
On Sunday, August 28, 2016 at 11:01:47 AM UTC-7, hughag...@gmail.com wrote:
> Well, I read Bernd Paysan's article on Mini-OOF:
> https://bernd-paysan.de/mini-oof.html
>
> His METHOD is somewhat cute, so I implemented it, but then I didn't like it and discarded it. The only way that I could implement it was with CREATE DOES>, and consequently it was grossly inefficient, so I got rid of it because that is not my style at all (grossly inefficient code is Bernd's style).
For the record, here is my METHOD code:
67896789 constant method-id \ used to make sure a method is actually a method; must be a distinctive value
0
w field .method-offset
w field .method-id
constant method-struct
: method ( offset -- new-offset ) \ like FIELD except only for pointers to functions
create
dup , w + \ -- new-offset \ .method-offset
method-id , \ -- new-offset \ .method-id
does> ( object base-adr -- )
.method-offset @ \ -- object field-offset
over + perform ;
: <set-method> ( xt object 'method -- )
>body
dup .method-id @ method-id <> abort" *** <SET-METHOD> given a method that wasn't actually a method ***"
.method-offset @ + ! ;
: set-method ( xt object -- ) \ stream: method-name
' <set-method> ;
: [set-method] ( xt object -- ) \ stream: method-name
postpone ['] postpone <set-method> ;
immediate
\ METHOD is like FIELD in that it is used to define a field. It doesn't take a field-size but assumes one cell.
\ The method, when executed, will EXECUTE whatever is stored in that field (unlike a field that provides the address).
\ [SET-METHOD] is used in constructors to set the method to a particular xt (the user doesn't have direct access to the offset).
As I said however, I have discarded this code because it is too inefficient. Any code that uses CREATE DOES> is going to be inefficient --- that is why CREATE DOES> is never used in the novice-package (except for DEFER which I provided because it is traditional, although I have VECTOR that is more efficient).
Not only is this code inefficient, but it also complicates the OOP package unnecessarily. The user has to learn about METHOD and [SET-METHOD] despite the fact that they don't really do anything for him. It is easier for the user to just learn how to use Forth in the usual way --- he can just learn the naming conventions that I use (as shown in the example code provided earlier) --- he doesn't have to learn any new words such as METHOD and [SET-METHOD] that are really just gratuitous Paysanesque complication.
[toc] | [prev] | [next] | [standalone]
| From | Mark Wills <markwills1970@gmail.com> |
|---|---|
| Date | 2016-08-18 02:22 -0700 |
| Message-ID | <2c76d425-84d2-4fef-b229-0906d2aad001@googlegroups.com> |
| In reply to | #50176 |
On Tuesday, 16 August 2016 22:20:10 UTC+1, hughag...@gmail.com wrote: > An argument that can be made against this system is that the first field of every object is now the pointer to the class of that object. The first field is faster than the rest (because the add of a zero offset gets optimized away), but now that is wasted on this pointer that is almost never used. For example, in LIST.4TH I had this: So use a negative offset. Your class words can still point to the first useful field.
[toc] | [prev] | [next] | [standalone]
| From | hughaguilar96@gmail.com |
|---|---|
| Date | 2016-08-18 12:02 -0700 |
| Message-ID | <a9c7c6b7-1de2-423e-b3c1-a2268c601b2a@googlegroups.com> |
| In reply to | #50204 |
On Thursday, August 18, 2016 at 2:22:14 AM UTC-7, Mark Wills wrote: > On Tuesday, 16 August 2016 22:20:10 UTC+1, hughag...@gmail.com wrote: > > An argument that can be made against this system is that the first field of every object is now the pointer to the class of that object. The first field is faster than the rest (because the add of a zero offset gets optimized away), but now that is wasted on this pointer that is almost never used. For example, in LIST.4TH I had this: > > So use a negative offset. Your class words can still point to the > first useful field. Yes, I could do that. I suppose I should. I already have ALLOCATE providing a memory block with a field underneath it which contains the size of the memory block --- ALLOCATION uses this field.
[toc] | [prev] | [next] | [standalone]
| From | hughaguilar96@gmail.com |
|---|---|
| Date | 2016-08-18 18:31 -0700 |
| Message-ID | <9b36075d-5bc4-4eea-a13d-796d49594398@googlegroups.com> |
| In reply to | #50204 |
On Thursday, August 18, 2016 at 2:22:14 AM UTC-7, Mark Wills wrote:
> On Tuesday, 16 August 2016 22:20:10 UTC+1, hughag...@gmail.com wrote:
> > An argument that can be made against this system is that the first field of every object is now the pointer to the class of that object. The first field is faster than the rest (because the add of a zero offset gets optimized away), but now that is wasted on this pointer that is almost never used. For example, in LIST.4TH I had this:
>
> So use a negative offset. Your class words can still point to the
> first useful field.
I decided against that for now. I already have these words in the novice-package:
: alloc ( size -- adr )
allocate abort" *** failure to allocate memory ***" ;
: dealloc ( adr -- ) \ the adr has to have been provided by ALLOCATE or RESIZE
?dup if free abort" *** failure to dealloc memory *** " then ;
: realloc ( adr size -- new-adr ) \ the adr has to have been provided by ALLOCATE or RESIZE
?dup if resize abort" *** failure to realloc memory ***" then ;
: zalloc ( size -- adr ) \ like ALLOC except also zeros it
dup alloc \ -- size adr
tuck swap erase ;
If I did what you said, then DEALLOC and REALLOC would no longer work with objects. I would need words specifically for objects --- such as KILL instead of DEALLOC --- but DEALLOC etc. would still work on other memory blocks, such as strings, that aren't objects.
What I'm thinking of now is writing a new package that is entirely oriented toward OOP --- everything would be a class, including strings --- this way I would have consistency throughout (nobody would use ALLOC DEALLOC etc. directly, but would only use class methods).
Such a pure-OOP package might be useful. What I have now in the novice-package is somewhat casual --- I'm not very consistent in regard to names --- for example, I have EACH rather than LIST.EACH (the naming convention is supposed to be the class name followed by a dot followed by the field or method name).
If I were going to write that book that Cecil suggested, "ANS-Forth --- a Guide to Alternate Realities," it would be best to have a consistent package with a consistent naming convention throughout, and with classes used throughout rather than just for some data types. I doubt that anybody in the year 2016 would actually read a book about ANS-Forth though (in the early 1990s such a book would have found some readership, although still not a likely candidate for the best-seller list).
Writing a book about general-purpose data-structures in ANS-Forth is like writing a book describing how to build a didgeridoo out of PVC pipe. All of your potential readers fall into one of these categories:
85% --- these have never heard of a didgeridoo; they don't care
14.9% --- these consider the didgeridoo to be ridiculous as a musical instrument because it has only one note, and it is out of tune; they will laugh at you
0.1% --- these are purists who believe that the didgeridoo must be made from the trunk of a eucalyptus tree that has been hollowed out by termites; they will hate you for using PVC pipe and will hurl vulgar insults at you (if they catch you playing your pvcidoo, they will take it away from you and beat you with it)
I would estimate the number of ANS-Forth programmers in the world today to be less than 20 (this is significantly below 0.1% of all programmers) --- all of them are here on comp.lang.forth and/or on the Forth-200x mailing-list --- all of them do a lot of noisy talking about how they set the Standard for the entire Forth community (they make so much noise as to drown out the rest of the Forth community who ignore ANS-Forth and who number 200 to 400).
The ANS-Forth enthusiasts are so full of hot air that they could play a didgeridoo continuously for an hour --- so ANS-Forth enthusiasts and didgeridoo enthusiasts have that in common too --- the didgeridoo enthusiasts out-number the ANS-Forth enthusiasts by 100 to 1 however, and they also tend to be more intelligent (some of them may know the basics of computer-science, which can't be said of the ANS-Forth enthusiasts).
[toc] | [prev] | [standalone]
Page 3 of 3 — ← Prev page 1 2 [3]
Back to top | Article view | comp.lang.forth
csiph-web