Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.forth > #50096 > unrolled thread

Re: CfV: Quotations

Started byBernd Paysan <bernd.paysan@gmx.de>
First post2016-08-14 20:52 +0000
Last post2016-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.


Contents

  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

#50096 — Re: CfV: Quotations

FromBernd Paysan <bernd.paysan@gmx.de>
Date2016-08-14 20:52 +0000
SubjectRe: 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]


#50105

Fromhughaguilar96@gmail.com
Date2016-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]


#50110

FromJulian Fondren <julian.fondren@gmail.com>
Date2016-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]


#50164

FromPaul Rubin <no.email@nospam.invalid>
Date2016-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]


#50168

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2016-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]


#50224

FromPaul Rubin <no.email@nospam.invalid>
Date2016-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]


#50252

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2016-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]


#50295

FromBernd Paysan <bernd.paysan@gmx.de>
Date2016-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]


#50326

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2016-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]


#50334

Fromhughaguilar96@gmail.com
Date2016-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]


#50450

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2016-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]


#50365

FromBernd Paysan <bernd.paysan@gmx.de>
Date2016-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]


#50406

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2016-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]


#50412

FromBernd Paysan <bernd.paysan@gmx.de>
Date2016-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]


#50449

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2016-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]


#50511

FromBernd Paysan <bernd@net2o.de>
Date2016-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]


#50512

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2016-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]


#50513

FromAndrew Haley <andrew29@littlepinkcloud.invalid>
Date2016-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]


#50514

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2016-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