Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.forth > #50096 > unrolled thread
| Started by | Bernd Paysan <bernd.paysan@gmx.de> |
|---|---|
| First post | 2016-08-14 20:52 +0000 |
| Last post | 2016-08-28 14:38 +0000 |
| Articles | 19 — 7 participants |
Back to article view | Back to comp.lang.forth
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Re: CfV: Quotations Bernd Paysan <bernd.paysan@gmx.de> - 2016-08-14 20:52 +0000
Re: CfV: Quotations hughaguilar96@gmail.com - 2016-08-14 14:42 -0700
Re: CfV: Quotations Julian Fondren <julian.fondren@gmail.com> - 2016-08-14 16:43 -0700
Re: CfV: Quotations Paul Rubin <no.email@nospam.invalid> - 2016-08-15 22:16 -0700
Re: CfV: Quotations anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-08-16 07:13 +0000
Re: CfV: Quotations Paul Rubin <no.email@nospam.invalid> - 2016-08-18 15:30 -0700
Re: CfV: Quotations anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-08-19 15:32 +0000
Re: CfV: Quotations Bernd Paysan <bernd.paysan@gmx.de> - 2016-08-20 20:46 +0000
Re: CfV: Quotations anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-08-21 09:32 +0000
Re: CfV: Quotations hughaguilar96@gmail.com - 2016-08-21 11:06 -0700
Re: CfV: Quotations anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-08-24 17:02 +0000
Re: CfV: Quotations Bernd Paysan <bernd.paysan@gmx.de> - 2016-08-21 22:52 +0000
Re: CfV: Quotations anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-08-22 13:54 +0000
Re: CfV: Quotations Bernd Paysan <bernd.paysan@gmx.de> - 2016-08-22 23:19 +0000
Re: CfV: Quotations anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-08-24 15:54 +0000
Re: CfV: Quotations Bernd Paysan <bernd@net2o.de> - 2016-08-28 00:43 +0000
Re: CfV: Quotations anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-08-28 09:53 +0000
Re: CfV: Quotations Andrew Haley <andrew29@littlepinkcloud.invalid> - 2016-08-28 08:32 -0500
Re: CfV: Quotations anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2016-08-28 14:38 +0000
| From | Bernd Paysan <bernd.paysan@gmx.de> |
|---|---|
| Date | 2016-08-14 20:52 +0000 |
| Subject | Re: CfV: Quotations |
| Message-ID | <noqlm8$ag1$3@dont-email.me> |
Am Wed, 10 Aug 2016 11:55:14 -0700 schrieb Paul Rubin: > I'm sympathetic to the calls for quotations to be able to access (at > least for reading) the parent locals, like C++ lambdas (C++'s version of > quotations) can do. I don't think comparisons with Lisp or Scheme are > that useful, but comparisons to C++ might be worthwhile. C/C++ has no stack the lambda would be able to access. E.g. if you want to count words in a wordlist with TRAVERSE-WORDLIST and quotations, you do : count-wl ( wid -- count ) >r 0 [: ( n nt -- n' flag ) drop 1+ true ;] r> traverse-wordlist ; Now do that in C++, and you need to access an outer local for that. You don't even have a chance to do it that way, because you can't extend the stack effect ( nt -- flag ) in a C++ function. In Forth, you can, and iterators can be written so that they keep their internal state off the data stack when calling the xt passed to them. Forth is not a locals-centric language, it's a stack-centric language. You can use the stack to pass data through quotations used e.g. as iterators. Without the stack, quotations would be much less useful. The problem with nested functions is not only that they are more difficult to implement, it's also that they can't outlive their parent's stack frame lifetime, and quotations which do are part of the use cases. This has been discussed to death already, with Hugh shouting at me for my "fake quotations". They are Forth quotations, not Lisp or C++ lambdas, because in Forth, we have stacks. And locals are an optional feature. -- Bernd Paysan "If you want it done right, you have to do it yourself" net2o ID: kQusJzA;7*?t=uy@X}1GWr!+0qqp_Cn176t4(dQ* http://bernd-paysan.de/
[toc] | [next] | [standalone]
| From | hughaguilar96@gmail.com |
|---|---|
| Date | 2016-08-14 14:42 -0700 |
| Message-ID | <903ddcc1-59cf-4088-afc2-8732fad021ca@googlegroups.com> |
| In reply to | #50096 |
On Sunday, August 14, 2016 at 1:52:25 PM UTC-7, Bernd Paysan wrote:
> Am Wed, 10 Aug 2016 11:55:14 -0700 schrieb Paul Rubin:
> > I'm sympathetic to the calls for quotations to be able to access (at
> > least for reading) the parent locals, like C++ lambdas (C++'s version of
> > quotations) can do. I don't think comparisons with Lisp or Scheme are
> > that useful, but comparisons to C++ might be worthwhile.
>
> C/C++ has no stack the lambda would be able to access. E.g. if you want
> to count words in a wordlist with TRAVERSE-WORDLIST and quotations, you do
>
> : count-wl ( wid -- count )
> >r 0 [: ( n nt -- n' flag ) drop 1+ true ;] r> traverse-wordlist ;
>
> Now do that in C++, and you need to access an outer local for that. You
> don't even have a chance to do it that way, because you can't extend the
> stack effect ( nt -- flag ) in a C++ function. In Forth, you can, and
> iterators can be written so that they keep their internal state off the
> data stack when calling the xt passed to them.
>
> Forth is not a locals-centric language, it's a stack-centric language.
> You can use the stack to pass data through quotations used e.g. as
> iterators.
>
> Without the stack, quotations would be much less useful.
>
> The problem with nested functions is not only that they are more
> difficult to implement, it's also that they can't outlive their parent's
> stack frame lifetime, and quotations which do are part of the use cases.
>
> This has been discussed to death already, with Hugh shouting at me for my
> "fake quotations". They are Forth quotations, not Lisp or C++ lambdas,
> because in Forth, we have stacks. And locals are an optional feature.
With the Paysan-faked quotations, locals are not an "optional feature" --- you don't provide any way for the quotation to access the parent function's locals, so locals are not an option at all --- by comparison, with my rquotations the user can either communicate via the data-stack or via locals.
Consider this code in which the rquotation communicates with the parent function via locals:
-----------------------------------------------------------------------------
seq
w field .gender \ 'M' or 'F' \ if someone isn't one or the other, then he/she isn't a person
constant person
: init-person ( str node -- node )
init-seq >r
[char] M r@ .gender ! \ default is male
r> ;
: new-person ( str -- node )
person alloc
init-person ;
person
w field .hotness \ scale of 1 to 10
constant chick
: init-chick ( hotness str node -- node )
init-person >r
[char] F r@ .gender !
r@ .hotness !
r> ;
: new-chick ( hotness str -- node )
chick alloc
init-chick ;
: test { head | people chicks tens nines eights -- }
head r[
1 +to people
dup .gender @ [char] F = if 1 +to chicks
.hotness @
dup 8 = if 1 +to eights then
dup 9 = if 1 +to nines then
10 = if 1 +to tens then \ Yay! We found at least one!
else drop then
]r |each
cr ." total people: " people .
cr ." total chicks: " chicks .
cr ." level 10 chicks: " tens .
cr ." level 9 chicks: " nines .
cr ." level 8 chicks: " eights .
;
-----------------------------------------------------------------------------
Here is a test:
-----------------------------------------------------------------------------
people show-seq
aaaaa
bbbbb
ccccc
Darby
eeeee
fffff
ggggg
hhhhh
ok
people test
total people: 8
total chicks: 5
level 10 chicks: 1
level 9 chicks: 0
level 8 chicks: 2 ok
-----------------------------------------------------------------------------
Now here is a light-weight function in which we don't use locals but rather just communicate via the data-stack (because this time we only have one counter that we are updating):
-----------------------------------------------------------------------------
: chick-counter ( head -- chicks )
0
swap r[ .gender @ [char] F = if 1+ then ]r |each ;
-----------------------------------------------------------------------------
Here is a test:
-----------------------------------------------------------------------------
people chick-counter ok-1
. 5 ok
-----------------------------------------------------------------------------
So, my rquotations are robust --- the user has the option of either communicating with the parent function via locals or via the data-stack --- by comparison, you force the user to always communicate via the data-stack, even when there are several data that need to be communicated (in my first example, the several counters).
All in all, I think that I'm way ahead of Forth-200x --- you aren't even close to having the capability that I have in the novice-package --- this wouldn't bother me, except for the fact that you insist on declaring everything that you do to be the Standard, effectively holding down the entire Forth community to your own level of incompetence.
BTW: I have noticed that every time the subject of quotations and HOFs comes up, you always trot out TRAVERSE-WORDLIST as your example HOF --- I get the impression that TRAVERSE-WORDLIST is the only HOF that you have ever written.
[toc] | [prev] | [next] | [standalone]
| From | Julian Fondren <julian.fondren@gmail.com> |
|---|---|
| Date | 2016-08-14 16:43 -0700 |
| Message-ID | <6d1603cc-8f2d-4c5b-8081-9604de8b5b06@googlegroups.com> |
| In reply to | #50105 |
On Sunday, August 14, 2016 at 4:42:13 PM UTC-5, hughag...@gmail.com wrote: > With the Paysan-faked quotations, locals are not an "optional feature" --- Locals are defined in the "The optional Locals word set" of ANS Forth. They are an "optional feature" to Forth. > > BTW: I have noticed that every time the subject of quotations and HOFs comes up, you always trot out TRAVERSE-WORDLIST as your example HOF --- I get the impression that TRAVERSE-WORDLIST is the only HOF that you have ever written. I can't read this as anything but a request for clf to tell you if you've worn out your one example and need to come up with another.
[toc] | [prev] | [next] | [standalone]
| From | Paul Rubin <no.email@nospam.invalid> |
|---|---|
| Date | 2016-08-15 22:16 -0700 |
| Message-ID | <87lgzxuwdr.fsf@jester.gateway.pace.com> |
| In reply to | #50096 |
Bernd Paysan <bernd.paysan@gmx.de> writes: > : count-wl ( wid -- count ) > >r 0 [: ( n nt -- n' flag ) drop 1+ true ;] r> traverse-wordlist ; > Now do that in C++, and you need to access an outer local for that. That would be a pretty unnatural interface for traverse-wordlist in C++, but I see what you mean. It effectively uses the stack as a shared global. I guess that's ok in this setting. In C++ traverse-wordlist would probably return an iterator and you'd loop over it or use std::count. > Forth is not a locals-centric language, it's a stack-centric > language... Without the stack, quotations would be much less useful. Locals are optional but I'd hope quotations are even more optional? > The problem with nested functions is not only that they are more > difficult to implement, it's also that they can't outlive their parent's > stack frame lifetime, and quotations which do are part of the use cases. I wonder how often quotations will leave stuff on the stack for use by the next iteration of something. I'm used to using a HOF like "fold" or "reduce" for that, though it would seem weird in Forth. If you don't need any mutable global state (like the stack) then you can pass parameters into the lambda, which can survive the parent returning.
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-08-16 07:13 +0000 |
| Message-ID | <2016Aug16.091357@mips.complang.tuwien.ac.at> |
| In reply to | #50164 |
Paul Rubin <no.email@nospam.invalid> writes:
>Bernd Paysan <bernd.paysan@gmx.de> writes:
>> Forth is not a locals-centric language, it's a stack-centric
>> language... Without the stack, quotations would be much less useful.
>
>Locals are optional but I'd hope quotations are even more optional?
I expect quotations to become CORE EXT, so there would be no
requirement to implement locals in order to implement quotations.
There is also no reason to, given that quotations cannot access outer
locals in the proposed quotations.
>> The problem with nested functions is not only that they are more
>> difficult to implement, it's also that they can't outlive their parent's
>> stack frame lifetime, and quotations which do are part of the use cases.
>
>I wonder how often quotations will leave stuff on the stack for use by
>the next iteration of something. I'm used to using a HOF like "fold" or
>"reduce" for that, though it would seem weird in Forth.
Depending on the HOF, but I expect it to happen pretty often for those
that call the xt multiple times. In Forth, you can use the same HOF
for foreach and for reduce.
>If you don't need any mutable global state (like the stack) then you can
>pass parameters into the lambda, which can survive the parent returning.
You can pass parameters through the stacks in addition to keeping
computed values on the stack. Of course, if there are too many,
things become unwieldy. E.g. (untested):
: fmap ( ... addr u xt -- ... )
\ addr u is a floating-point array with u elements starting at addr.
\ call xt ( ... r -- ... ) once for each element r
... ;
2e 0e v vlen [: ( rexp racc r -- rexp racc1 ) 2 fpick f** f+ :] fmap
Bernd Paysan thinks that's good enough. I am not sure. Especially
when working with several HOFs at once, it becomes hard to follow what
happens on the stack. Or maybe it's just that we are not used to
dealing with that.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2016: http://www.euroforth.org/ef16/
[toc] | [prev] | [next] | [standalone]
| From | Paul Rubin <no.email@nospam.invalid> |
|---|---|
| Date | 2016-08-18 15:30 -0700 |
| Message-ID | <874m6hvhfc.fsf@jester.gateway.pace.com> |
| In reply to | #50168 |
anton@mips.complang.tuwien.ac.at (Anton Ertl) writes: > I expect quotations to become CORE EXT Hmm, that surprises me a bit, since quotations seem like an esoteric feature for most Forth users. But I guess the implementation can be very simple.. > so there would be no requirement to implement locals in order to > implement quotations. IMO locals are much more useful, so regardless of implementation dependencies, if I had to pick just one I'd pick locals. > 2e 0e v vlen [: ( rexp racc r -- rexp racc1 ) 2 fpick f** f+ :] fmap > Bernd Paysan thinks that's good enough. I am not sure. Especially > when working with several HOFs at once, it becomes hard to follow what > happens on the stack. Or maybe it's just that we are not used to > dealing with that. That example is readable but yes, it does get worse with deeper levels of HOF. Witness Haskell vs Python: the languages have similar raw capabilities, but Haskell uses HOFs more freely because its type system keeps them from getting out of control. I think that Forthers generally don't want to use so much abstraction so maybe it won't be an issue. In fact I wonder if we language geeks are irresistably drawn to features that working practitioners (hardware designers etc.) don't particularly care about. The whole quotation discussion could be an example of that.
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-08-19 15:32 +0000 |
| Message-ID | <2016Aug19.173224@mips.complang.tuwien.ac.at> |
| In reply to | #50224 |
Paul Rubin <no.email@nospam.invalid> writes:
>anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
>> I expect quotations to become CORE EXT
>
>Hmm, that surprises me a bit, since quotations seem like an esoteric
>feature for most Forth users.
CORE EXT is the typical place for things that do not belong anywhere
else. Another place might be TOOLS EXT, but looking at 15.1 and
15.6.1, I don't think that's the right place (but the same could be
said of a number of things in TOOLS EXT; e.g., AHEAD, CODE, ;CODE
etc. are not just used during development, but also in the finished
applications.
Anyway, if we put it in TOOLS EXT, you can still have a standard
system with quotations and without locals.
>> 2e 0e v vlen [: ( rexp racc r -- rexp racc1 ) 2 fpick f** f+ :] fmap
>
>> Bernd Paysan thinks that's good enough. I am not sure. Especially
>> when working with several HOFs at once, it becomes hard to follow what
>> happens on the stack. Or maybe it's just that we are not used to
>> dealing with that.
>
>That example is readable but yes, it does get worse with deeper levels
>of HOF. Witness Haskell vs Python: the languages have similar raw
>capabilities, but Haskell uses HOFs more freely because its type system
>keeps them from getting out of control.
I think that the tendency of Haskell to use currying (coming from the
syntax) plays a bigger role, as well as the culture of functional
programming.
>I think that Forthers generally don't want to use so much abstraction so
>maybe it won't be an issue. In fact I wonder if we language geeks are
>irresistably drawn to features that working practitioners (hardware
>designers etc.) don't particularly care about. The whole quotation
>discussion could be an example of that.
Certainly, looking at the current CfV results, there seem to be a
bunch of people who have already used quotations, and a bunch of
people who say they will never use quotations. I have worked around
their absence (by defining helper words) for decades, other people
have worked around their absence with return-address maniupulations,
so I don't think that practitioners don't care about them, unless you
define a practitioner to be someone who does not care about
quotations.
Looking at other languages, closures have been limited to functional
languages for a long time, but a few years ago, "lambdas" started
appearing in C#, then in Java and C++. I guess there's a contingent
of programmers in these languages who don't need lambdas and won't use
it, but others use lambdas; it's probably too early to tell the
overall effect of lambda on these languages.
An earlier innovation was genericity; at the start this was probably
also something only for language geeks, but I think it's now
mainstream in Java, C++, and C#. Just if anybody wonders if we need
this in Forth: Genericity is a solution for a problem that these
languages get from their static typechecking; Forth has no
typechecking, so it does not need to add genericity to the type
system.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2016: http://www.euroforth.org/ef16/
[toc] | [prev] | [next] | [standalone]
| From | Bernd Paysan <bernd.paysan@gmx.de> |
|---|---|
| Date | 2016-08-20 20:46 +0000 |
| Message-ID | <npafji$59r$1@dont-email.me> |
| In reply to | #50168 |
Am Tue, 16 Aug 2016 07:13:57 +0000 schrieb Anton Ertl: > : fmap ( ... addr u xt -- ... ) > \ addr u is a floating-point array with u elements starting at addr. \ > call xt ( ... r -- ... ) once for each element r > ... ; > > 2e 0e v vlen [: ( rexp racc r -- rexp racc1 ) 2 fpick f** f+ :] fmap > > Bernd Paysan thinks that's good enough. I am not sure. Especially when > working with several HOFs at once, it becomes hard to follow what > happens on the stack. Or maybe it's just that we are not used to > dealing with that. As usual in Forth, you shouldn't deal with "too many variables" (see Thinking Forth page 210 in the online edition), nor should you write a word with HOFs inside HOFs inside HOFs. If you really need a complex data structure, use and OOP package, best one with a current object, because that current object will be current inside the quotation, too, without any stack juggling. -- Bernd Paysan "If you want it done right, you have to do it yourself" net2o ID: kQusJzA;7*?t=uy@X}1GWr!+0qqp_Cn176t4(dQ* http://bernd-paysan.de/
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-08-21 09:32 +0000 |
| Message-ID | <2016Aug21.113258@mips.complang.tuwien.ac.at> |
| In reply to | #50295 |
Bernd Paysan <bernd.paysan@gmx.de> writes:
>Am Tue, 16 Aug 2016 07:13:57 +0000 schrieb Anton Ertl:
>> Bernd Paysan thinks that's good enough. I am not sure. Especially when
>> working with several HOFs at once, it becomes hard to follow what
>> happens on the stack. Or maybe it's just that we are not used to
>> dealing with that.
>
>As usual in Forth, you shouldn't deal with "too many variables" (see
>Thinking Forth page 210 in the online edition),
This talks about global variables, and says nothing about "too many
variables". Maybe you mean the cartoon on Page 209, but that does not
present a solution to the problem.
>nor should you write a
>word with HOFs inside HOFs inside HOFs.
HOFs inside HOFs are easy. There is only one HOF to consider at each
point in this case. It's harder if you pass HOFs to HOFs.
Anyway, if a Forther gives me "you should not"s without a supporting
argument, nor presenting an alternative approach, it's an indication
that there is some lack in the language. So maybe keeping the
not-quite-local data on the data stack is not enough to make accesses
to outer locals unnecessary.
>If you really need a complex data structure, use and OOP package, best
>one with a current object, because that current object will be current
>inside the quotation, too, without any stack juggling.
Ok, so your solution is to have one global variable, and access all
the other data through that. I very much doubt that is sufficient,
because global variables are not sufficient to implement nested
Pascal's and GNU C's nested functions, either; instead, they do static
link chains and trampolines. E.g, I don't think you can do the
man-or-boy-test with that.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2016: http://www.euroforth.org/ef16/
[toc] | [prev] | [next] | [standalone]
| From | hughaguilar96@gmail.com |
|---|---|
| Date | 2016-08-21 11:06 -0700 |
| Message-ID | <b2548da8-fc99-46d1-8323-f32dcb3e336f@googlegroups.com> |
| In reply to | #50326 |
On Sunday, August 21, 2016 at 2:57:30 AM UTC-7, Anton Ertl wrote: > Bernd Paysan <bernd.paysan@gmx.de> writes: > >nor should you write a > >word with HOFs inside HOFs inside HOFs. > > HOFs inside HOFs are easy. There is only one HOF to consider at each > point in this case. It's harder if you pass HOFs to HOFs. The phrase "HOFs inside HOFs" makes no sense --- you can have quotations inside of functions, but HOFs are named functions which are not inside of other functions --- I've noticed before that you guys' terminology is mixed up; you seem to not know the difference between a HOF and a quotation. These are my rules for rquotations: 1.) The rquotation must be called with REX rather than EXECUTE. 2.) Only a HOF can use REX. 3.) A HOF can not call another HOF. 4.) A HOF must have local variables. If you do this, then the rquotation can access the parent function's local variables. These are easy rules to follow because this is the way that you would want to use quotations almost all of the time anyway.
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-08-24 17:02 +0000 |
| Message-ID | <2016Aug24.190223@mips.complang.tuwien.ac.at> |
| In reply to | #50334 |
hughaguilar96@gmail.com writes:
>On Sunday, August 21, 2016 at 2:57:30 AM UTC-7, Anton Ertl wrote:
>> Bernd Paysan <bernd.paysan@gmx.de> writes:
>> >nor should you write a=20
>> >word with HOFs inside HOFs inside HOFs.
>>=20
>> HOFs inside HOFs are easy. There is only one HOF to consider at each
>> point in this case. It's harder if you pass HOFs to HOFs.
>
>The phrase "HOFs inside HOFs" makes no sense
What I meant is a call of a higher-order word inside a higher-order
word. A typical example is
: with-base ( n xt -- )
base @ >r
[: swap base ! execute ;] catch
r> base ! throw ;
12345 $10 ' . with-base
WITH-BASE takes and xt and is therefore a higher-order word, and so is
CATCH.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2016: http://www.euroforth.org/ef16/
[toc] | [prev] | [next] | [standalone]
| From | Bernd Paysan <bernd.paysan@gmx.de> |
|---|---|
| Date | 2016-08-21 22:52 +0000 |
| Message-ID | <npdbbl$vqt$2@dont-email.me> |
| In reply to | #50326 |
Am Sun, 21 Aug 2016 09:32:58 +0000 schrieb Anton Ertl:
> Ok, so your solution is to have one global variable, and access all the
> other data through that. I very much doubt that is sufficient, because
> global variables are not sufficient to implement nested Pascal's and GNU
> C's nested functions, either; instead, they do static link chains and
> trampolines. E.g, I don't think you can do the man-or-boy-test with
> that.
Well, the boy-or-man test isn't really needing better quotations, it's
needing a bit more OOP. It wants something applied in a "context", and
we call that "method". While you could have many methods, here, one is
sufficient. While you could have many different object lifetime schemes,
stack-allocated here is sufficient.
require mini-oof2.fs
\ define a class for the default values
object class
field: k
method b
end-class mob
:noname ( -- n ) k @ ; mob to b
mob new constant m-1 -1 m-1 .k !
mob new constant m0 0 m0 .k !
mob new constant m1 1 m1 .k !
\ define a class for the context
mob class
field: x1
field: x2
field: x3
field: x4
field: x5
end-class b-ctx
: a ( k x1 x2 x3 x4 x5 -- result )
b-ctx new >o
x5 ! x4 ! x3 ! x2 ! x1 ! k !
k @ 0<= IF
x4 @ .b x5 @ .b +
ELSE
b
THEN
dispose o> ;
:noname ( -- n )
-1 k +!
k @ o x1 @ x2 @ x3 @ x4 @ a
; b-ctx to b
: a-n ( n -- n' )
m1 m-1 m-1 m1 m0 a ;
Test with a bit more return stack space than usual (I just set it to 1G):
27 0 [do] cr [i] dup . a-n . [lOOP]
0 1
1 0
2 -2
3 0
4 1
5 0
6 1
7 -1
8 -10
9 -30
10 -67
11 -138
12 -291
13 -642
14 -1446
15 -3250
16 -7244
17 -16065
18 -35601
19 -78985
20 -175416
21 -389695
22 -865609
23 -1922362
24 -4268854
25 -9479595
26 -21051458 ok
Looks good. If you want OOP, use OOP. If you want something that is
slightly less than OOP, just use OOP, too, please. Unlike nested
functions, OOP is dead easy to implement. It really is powerful enough,
way more powerful than nested functions, as powerful as closures. As
quotations are mostly syntactic sugar, they are not needed at all here.
Maybe you want an object on the locals stack to get rid of the relatively
expensive new/dispose pairs.
--
Bernd Paysan
"If you want it done right, you have to do it yourself"
net2o ID: kQusJzA;7*?t=uy@X}1GWr!+0qqp_Cn176t4(dQ*
http://bernd-paysan.de/
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-08-22 13:54 +0000 |
| Message-ID | <2016Aug22.155432@mips.complang.tuwien.ac.at> |
| In reply to | #50365 |
Bernd Paysan <bernd.paysan@gmx.de> writes:
>Am Sun, 21 Aug 2016 09:32:58 +0000 schrieb Anton Ertl:
>
>> Ok, so your solution is to have one global variable, and access all the
>> other data through that. I very much doubt that is sufficient, because
>> global variables are not sufficient to implement nested Pascal's and GNU
>> C's nested functions, either; instead, they do static link chains and
>> trampolines. E.g, I don't think you can do the man-or-boy-test with
>> that.
>
>Well, the boy-or-man test isn't really needing better quotations, it's
>needing a bit more OOP.
Sure, you can implement the functionality in some way in any
Touring-complete language. But the original test was for whether
nested functions in Algol 60 were correctly implemented. So if it can
be implemented with closures (actually Pascal function parameters are
sufficient), but not with the proposed quotations, it shows that
passing data on the stack does not replace non-local data accesses in
every case. Of course, as is often the case in Forth, the question is
if the various approaches for avoiding a feature are sufficient to
make that feature unnecessary or not.
>require mini-oof2.fs
>
>\ define a class for the default values
>
>object class
> field: k
> method b
>end-class mob
>
>:noname ( -- n ) k @ ; mob to b
>mob new constant m-1 -1 m-1 .k !
>mob new constant m0 0 m0 .k !
>mob new constant m1 1 m1 .k !
>
>\ define a class for the context
>
>mob class
> field: x1
> field: x2
> field: x3
> field: x4
> field: x5
>end-class b-ctx
>
>: a ( k x1 x2 x3 x4 x5 -- result )
> b-ctx new >o
> x5 ! x4 ! x3 ! x2 ! x1 ! k !
> k @ 0<= IF
> x4 @ .b x5 @ .b +
> ELSE
> b
> THEN
> dispose o> ;
>
>:noname ( -- n )
> -1 k +!
> k @ o x1 @ x2 @ x3 @ x4 @ a
>; b-ctx to b
>
>: a-n ( n -- n' )
> m1 m-1 m-1 m1 m0 a ;
For comparsion, here's the Algol 60 variant:
begin
real procedure A(k, x1, x2, x3, x4, x5);
value k; integer k;
begin
real procedure B;
begin k := k - 1;
B := A := A(k, B, x1, x2, x3, x4);
end;
if k <= 0 then A := x4 + x5 else B;
end;
outreal(A(10, 1, -1, -1, 1, 0));
end;
and my attempt at a version in Forth with access to outer locals,
based on <http://rosettacode.org/wiki/Man_or_boy_test#Pascal> (my
Algol 60 is not good enough to grasp the fine details of the Algol 60
version):
0 constant zero
1 constant one
-1 constant negone
: A {: k x1 x2 x3 x4 x5 | B -- n :} recursive
[: k 1- to k
k B x1 x2 x3 x4 A ;] to B
k 0 <= if
x4 execute x5 execute +
else
B execute
then ;
10 ' one ' negone ' negone ' one ' zero A .
So access to outer locals makes this quite a bit shorter (14 instead
of 41 lines) than making the frames explicit by coding them in OOP.
The question is how often that helps for real-world programs. My
guess is that, with the Pascal-like restriction, not often enough to
make it worthwhile. With garbage-collection of closures, it would
probably be worthwhile, but we probably don't want to go there; and
for something in between, we will have to see.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2016: http://www.euroforth.org/ef16/
[toc] | [prev] | [next] | [standalone]
| From | Bernd Paysan <bernd.paysan@gmx.de> |
|---|---|
| Date | 2016-08-22 23:19 +0000 |
| Message-ID | <npg1ao$29b$1@dont-email.me> |
| In reply to | #50406 |
Am Mon, 22 Aug 2016 13:54:32 +0000 schrieb Anton Ertl:
> The question is how often that helps for real-world programs. My guess
> is that, with the Pascal-like restriction, not often enough to make it
> worthwhile. With garbage-collection of closures, it would probably be
> worthwhile, but we probably don't want to go there; and for something in
> between, we will have to see.
MINOS uses a lot of closure-like objects+quotations, the "actions". An
action is bound to a widget, contains a state variable of its own (e.g.
on/off for a toggle button), and an action (or two), stored as quotation
xt; the quotation is performed in the context of the object that created
those actions and widgets. Since all that commons stuff is reused again
and again, it's just a slightly different version of [: ;] around a block
of code to instantiate them. So the line count is probably not a problem.
In Common Lisp, you would do that with closures. In an OOP system, you
do it with OOP. And if you want a more compact format of declaring
things, just define them. You could have an OOP system that declares the
class in one line, and creates you an instantiation code that takes the
initial values from stack, just with about the same amount of code we
need to implement locals (a lot more code than mini-oof2.fs).
<base class> O{ k x1 x2 x3 x4 x5 }O mob
certainly is possible. There's even a mini-oof.fs based implementation
of closures, which does just that. If we wanted to generalize the locals
declaration, we could easily reuse most of the code in glocals.fs; after
all, the declaration of a set of locals and the declaration of a set of
instance variables is the same problem: Create a bunch of names which
address consecutive elements in memory; one time on a locals stack,
another time in an object. And Algol 60 even recognizes that by calling
the locals "activation record".
If you look at the Rosetta code page of the man-or-boy test, you'll find
a number of implementations which use classes to implement the activation
record, so I think that approach is fair and useful.
http://rosettacode.org/wiki/Man_or_boy_test
If you want a class creation syntax which reduces line count, it's also
doable. mini-oof2.fs just shares code with the structure declaration,
sharing code with the locals declaration would also be possible.
--
Bernd Paysan
"If you want it done right, you have to do it yourself"
net2o ID: kQusJzA;7*?t=uy@X}1GWr!+0qqp_Cn176t4(dQ*
http://bernd-paysan.de/
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-08-24 15:54 +0000 |
| Message-ID | <2016Aug24.175406@mips.complang.tuwien.ac.at> |
| In reply to | #50412 |
Bernd Paysan <bernd.paysan@gmx.de> writes:
>In Common Lisp, you would do that with closures. In an OOP system, you
>do it with OOP.
Somehow, in the last few years, several popular object-oriented
languages, e.g., Java, C++, and C#, have acquired lambdas; apparently
not everybody does it with OOP.
And actually you are not using the distinctive feature of OOP: dynamic
binding. What you use, is the current-object pointer. However, this
allows access only to locals from one activation record; that's enough
for the man-or-boy test, but not in the general case. Is the general
case needed? Yes, in particular in curried functions each argument is
in a separate activation record.
>And if you want a more compact format of declaring
>things, just define them. You could have an OOP system that declares the
>class in one line, and creates you an instantiation code that takes the
>initial values from stack, just with about the same amount of code we
>need to implement locals (a lot more code than mini-oof2.fs).
>
><base class> O{ k x1 x2 x3 x4 x5 }O mob
>
>certainly is possible. There's even a mini-oof.fs based implementation
>of closures, which does just that. If we wanted to generalize the locals
>declaration, we could easily reuse most of the code in glocals.fs; after
>all, the declaration of a set of locals and the declaration of a set of
>instance variables is the same problem: Create a bunch of names which
>address consecutive elements in memory; one time on a locals stack,
>another time in an object.
Is the locals-derived "class" definition useful for anything besides
locals?
Of course, if we support non-local locals, we will have to enhance the
locals code to create activation records when needed, and also to
create the access to the right activation record. The definition part
is similar to a structure or class definition, but the access part is
quite different for the general case.
We also need a way to specify how an activation record is allocated
and how long it lives. E.g., for something CREATE-DOES>-like we want
to ALLOT the memory:
: constant ( x "name" -- )
['] here-allot <{: x :}
[: x :] <create ;
This would allot the activation record (and HERE-ALLOT is an ALLOT
that returns the address of the allotted memory), and at run-time it
accesses the local and return it.
>If you look at the Rosetta code page of the man-or-boy test, you'll find
>a number of implementations which use classes to implement the activation
>record, so I think that approach is fair and useful.
I also find that the C++11 and C# version 3+ have shorter source code
by using anonymous functions/lambda expressions.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2016: http://www.euroforth.org/ef16/
[toc] | [prev] | [next] | [standalone]
| From | Bernd Paysan <bernd@net2o.de> |
|---|---|
| Date | 2016-08-28 00:43 +0000 |
| Message-ID | <nptc44$o3r$2@dont-email.me> |
| In reply to | #50449 |
Am Wed, 24 Aug 2016 15:54:06 +0000 schrieb Anton Ertl:
> And actually you are not using the distinctive feature of OOP: dynamic
> binding. What you use, is the current-object pointer. However, this
> allows access only to locals from one activation record; that's enough
> for the man-or-boy test, but not in the general case. Is the general
> case needed? Yes, in particular in curried functions each argument is
> in a separate activation record.
What about this part of my source code was not clear to you?
object class
field: k
method b
end-class mob
:noname ( -- n ) k @ ; mob to b \ bind xt to b of mob
...
:noname ( -- n )
-1 k +!
k @ o x1 @ x2 @ x3 @ x4 @ a
; b-ctx to b \ bind xt to b of b-ctx
Method b is dynamically bound to either work for the constants or for the
recursive call.
Note that the lambda closures in C++ and Java are syntactic sugar around
classes, they aren't implemented the same way as you would do it in Algol
60. C++ overloads the function call to make it happen, which actually
means that you can't pass a lambda closure to a function that takes a
normal function pointer.
Only lambdas without any captured variables are compatible with function
pointers, all others need std::function as type.
http://www.cprogramming.com/c++11/c++11-lambda-closures.html
In Java, it was easier, as Java didn't have any function pointer type to
start with. Now it has one, and it's just calling a specified method of
a particular class, something Java's VM can do.
--
Bernd Paysan
"If you want it done right, you have to do it yourself"
net2o ID: kQusJzA;7*?t=uy@X}1GWr!+0qqp_Cn176t4(dQ*
http://bernd-paysan.de/
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-08-28 09:53 +0000 |
| Message-ID | <2016Aug28.115350@mips.complang.tuwien.ac.at> |
| In reply to | #50511 |
Bernd Paysan <bernd@net2o.de> writes:
>Am Wed, 24 Aug 2016 15:54:06 +0000 schrieb Anton Ertl:
>
>> And actually you are not using the distinctive feature of OOP: dynamic
>> binding. What you use, is the current-object pointer. However, this
>> allows access only to locals from one activation record; that's enough
>> for the man-or-boy test, but not in the general case. Is the general
>> case needed? Yes, in particular in curried functions each argument is
>> in a separate activation record.
>
>What about this part of my source code was not clear to you?
What makes you think that it is not clear to me?
>Note that the lambda closures in C++ and Java are syntactic sugar around
>classes, they aren't implemented the same way as you would do it in Algol
>60.
Earlier you argued that activation records and objects are the same thing.
I see two differences:
* Objects are connected to dynamic binding of methods, something that
is not needed for activation records. But that extra capability
does not hurt when implementing activation records with objects.
* Object-oriented languages normally have only one current object
(this or self), while there several activation records can be active
at the same time, one for each outer function that has local
variables. That is what I was pointing out above.
As a minimal example, consider a curried implementation
of ROT in a dialect of Forth with full closures:
: crot { x } [: { y } [: { z } y x z ;] ;] ;
1 2 3 crot execute execute \ now on the stack: 2 3 1
Here you have an activation record containing X, and another
activation record containing Y, and you cannot keep both of those in
the current object, but need to implement some additional mechanism
to perform the access; in cases with read-only locals like this one,
you can shift the additional work to the definition (by also
defining a copy of X in the Y activation record, and maybe copies of
X and Y in the Z activation record).
But in cases with locals that are changed, you cannot do that. The
classical implementations are to use a static link chain or a
display, but the C++ syntax suggests keeping the addresses of the
accessed variables in the later activation records (one can also see
this as a variation on the display theme). Anyway, all of this
requires something that's a bit different from an ordinary
object-oriented field access.
>C++ overloads the function call to make it happen, which actually
>means that you can't pass a lambda closure to a function that takes a
>normal function pointer.
>
>Only lambdas without any captured variables are compatible with function
>pointers, all others need std::function as type.
>
>http://www.cprogramming.com/c++11/c++11-lambda-closures.html
My guess is that this difference between ordinary function pointers
and std::function is that ordinary function pointers are C-compatible
and don't come with an environment, while std:function includes an
environment. They apparently did not want to go for trampolines, and,
after all they are C++: Introducing yet another type and then trying
to hide it with overloading is their way of life.
>In Java, it was easier, as Java didn't have any function pointer type to
>start with. Now it has one, and it's just calling a specified method of
>a particular class, something Java's VM can do.
The hard part for Java apparently was to make it fit in the type
system (at least my colleague who is into type systems tells me that).
Fortunately we don't have that problem in Forth.
Anyway, both languages have added lambdas, however implemented,
because they allow expressing programs more succinctly than with just
the object-oriented features they had before.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2016: http://www.euroforth.org/ef16/
[toc] | [prev] | [next] | [standalone]
| From | Andrew Haley <andrew29@littlepinkcloud.invalid> |
|---|---|
| Date | 2016-08-28 08:32 -0500 |
| Message-ID | <yeKdndqPD8JPel_KnZ2dnUU7-WvNnZ2d@supernews.com> |
| In reply to | #50512 |
Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
>
>>In Java, it was easier, as Java didn't have any function pointer type to
>>start with. Now it has one, and it's just calling a specified method of
>>a particular class, something Java's VM can do.
>
> The hard part for Java apparently was to make it fit in the type
> system (at least my colleague who is into type systems tells me
> that).
> Fortunately we don't have that problem in Forth.
I'm not sure what problem that might have been. This is a declaration
of a type "function int -> int":
interface IntToIntFunction {
int apply(int n);
}
and here's the declaration of a function of that type:
IntToIntFunction square = (x -> x * x);
This declaration is the same as:
IntToIntFunction square = new IntToIntFunction() {
public int apply(int n) { return n * n; }
};
No change to the Java type system was needed.
I don't think that this decision was very controversial in the end
because there were already Java libraries which used this kind of
interface and nobody wanted to write new "lambda-ified" libraries.
And, of course, this was easy for Java programmers to understand.
Andrew.
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2016-08-28 14:38 +0000 |
| Message-ID | <2016Aug28.163830@mips.complang.tuwien.ac.at> |
| In reply to | #50513 |
Andrew Haley <andrew29@littlepinkcloud.invalid> writes:
>Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
>>
>>>In Java, it was easier, as Java didn't have any function pointer type to
>>>start with. Now it has one, and it's just calling a specified method of
>>>a particular class, something Java's VM can do.
>>
>> The hard part for Java apparently was to make it fit in the type
>> system (at least my colleague who is into type systems tells me
>> that).
>> Fortunately we don't have that problem in Forth.
>
>I'm not sure what problem that might have been.
Sorry, I am not into this, so I cannot explain it to you.
However, even arrays of objects have often given me type trouble in
Java, and he explained that this had to do with IIRC
co/contravariance, so I can easily believe that designing lambdas in a
way that fits with Javas type system was a non-trivial exercise; after
all, java takes type checking seriously, and does not have the C++
escape hatch of declaring undefined behaviour when the going gets
tough. Of course, once they had such a design, it just works.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2016: http://www.euroforth.org/ef16/
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.forth
csiph-web