Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Currying puzzle

369 views
Skip to first unread message

Ruvim

unread,
Oct 8, 2018, 11:53:59 AM10/8/18
to
There is a Currying section On Rosetta Code website.
https://rosettacode.org/wiki/Currying

The most examples there are incorrect. They shows partial application
instead of currying.

"partial" function takes several arguments and a function and produces
another function.
partial ( i*x xt i -- xt1 )

"partial1" fixes one top argument:
partial1 ( x1 xt -- xt2 )
\ xt2 has semantics "x1 xt execute"

"partial2" fixes two top arguments:
partial2 ( x2 x1 xt -- xt2 )
\ xt2 has semantics "x2 x1 xt execute"

"partial3" fixes three top arguments:
partial2 ( x3 x2 x1 xt -- xt2 )
\ xt2 has semantics "x3 x2 x1 xt execute"


"curry" function takes a function and the number of its arguments and
produces another function.
curry ( xt u -- xt1 )

"curry1" takes a function (that has one argument) and produces another
function that evaluates the first one. So, it may do nothing.
curry1 ( xt -- xt1 )
\ xt1 ( x -- result ) \ xt1 has semantics: "xt execute"

"curry2" takes a function (that has two arguments) and produces another
function of one argument that produces another function of one argument
that evaluates the first one.
curry2 ( xt -- xt1 )
\ xt1 ( x1 -- xt2 )
\ xt2 ( x2 -- result ) \ xt2 has semantics: "x1 xt execute"
\ Evidently, xt2 can be produced via "x1 xt partial1"

"curry3" takes a function of three arguments:
curry3 ( xt -- xt1 )
\ xt1 ( x1 -- xt2 )
\ xt2 ( x2 -- xt3 )
\ xt3 ( x3 -- result ) \ xt3 has semantics: "x2 x1 xt execute"
\ Evidently, xt3 can be produced via "x2 x1 xt partial2"




The *puzzles* are

1. define "curry2" and "curry3" words
2. define "curry3" via "curry2"
3. define general "curry" word ( xt u -- xt1 )



In Haskell any function is already curried.
Conceptually (not using Haskell syntax):
f(a)(b) === f(a,b)
f(a)(b)(c) === f(a,b,c)

In ECMAScript:
let curry2 = f => a => b => f(a, b);
let curry3 = f => a => b => c => f(a, b, c);

curry3( (a,b,c) => (a*b+c) )(2)(3)(4) === 10



\ testcase for Forth

: a ( xt x1 -- x ) swap execute ;

: muladd * + ; ' muladd curry3 2 a 3 a 4 a . cr \ prints 10


--
Ruvim

Julian Fondren

unread,
Oct 8, 2018, 12:52:29 PM10/8/18
to
On Monday, October 8, 2018 at 10:53:59 AM UTC-5, Ruvim wrote:
> There is a Currying section On Rosetta Code website.
> https://rosettacode.org/wiki/Currying
>
> The most examples there are incorrect. They shows partial application
> instead of currying.

They do that because partial application is what anyone actually *wants*
from currying, and once you realize that, it's also an easier and more
readable and more efficient feature to directly provide.

For a language without currying to provide partial application via currying
is like someone thinking "to go north, I need to first go east" - because
his friend, who lives in the east, always goes north "from the east".

Actually, there's nothing blocking you. North is that way. Just go.

> \ testcase for Forth
>
> : a ( xt x1 -- x ) swap execute ;
>
> : muladd * + ; ' muladd curry3 2 a 3 a 4 a . cr \ prints 10

What is that you want from currying?

Ruvim

unread,
Oct 8, 2018, 5:00:54 PM10/8/18
to
On 2018-10-08 19:52, Julian Fondren wrote:
> On Monday, October 8, 2018 at 10:53:59 AM UTC-5, Ruvim wrote:
>> There is a Currying section On Rosetta Code website.
>> https://rosettacode.org/wiki/Currying
>>
>> The most examples there are incorrect. They shows partial application
>> instead of currying.
>
> They do that because partial application is what anyone actually *wants*
> from currying,

I disagree. There is a special section for Partial application examples:
https://rosettacode.org/wiki/Partial_function_application

There is no sense to have identical examples in these different sections.


> and once you realize that, it's also an easier and more
> readable and more efficient feature to directly provide.
>
> For a language without currying to provide partial application via currying
> is like someone thinking "to go north, I need to first go east" - because
> his friend, who lives in the east, always goes north "from the east".
>
> Actually, there's nothing blocking you. North is that way. Just go.

Actually, one can be defined via another. "partial" via "curry" and
"curry" via "partial" (in Forth at least)


Well, slightly easier puzzles.

1. define "partial1", "partial2", "partial3" words.
2. define "partial2" using "partial1", and "partial3" using "partial2"
3. define "partial" word ( i*x xt i -- xt1 )


>> \ testcase for Forth
>>
>> : a ( xt x1 -- x ) swap execute ;
>>
>> : muladd * + ; ' muladd curry3 2 a 3 a 4 a . cr \ prints 10
>
> What is that you want from currying?
>

It is just a *programming puzzle*. For fun, for learning, and to
challenge yourself.



I defined the useful wrappers that can be used in the place of
:NONAME ... ;
These wrappers are transparent regarding the data stack and can be nested.

https://github.com/ruv/forth-design-exp/blob/master/lexeme-translator/advanced.example.fth
(this Forth extension should work on any ANS Forth system, use index.fth
to load).


The code
123 p{ lit{} ... }p
is functionally equal to
123 >r :noname [ r> ] literal ... ;

The code
p{ 123 . } p{ call{} ... }p
is functionally equal to
:noname 123 . ; >r :noname [ r> compile, ] ... ;


The code
123 p{ p{ lit{ lit{} }lit . }p }p execute execute
is equal to
123 p{ lit{} p{ lit{} . }p }p execute execute
and prints 123

So, it is the closures. In Gforth, using its recent closures module,
this code fragment is equal to:
[: 123 [{: x :}d x [{: x :}d x . ;] ;] ;] execute execute execute




Actually I just realized that it is difficult to find a way to define
curry3 via curry2. I only found the solution with help of a kind of
algebraic transformations (i.e. using only the rules of forms
transformation, without taking semantics into account).

Perhaps it is not a challenge for you?


--
Ruvim

Albert van der Horst

unread,
Oct 8, 2018, 5:24:58 PM10/8/18
to
In article <ppfuij$17r6$1...@gioia.aioe.org>,
Ruvim <ruvim...@gmail.com> wrote:
>There is a Currying section On Rosetta Code website.
>https://rosettacode.org/wiki/Currying
>
>The most examples there are incorrect. They shows partial application
>instead of currying.

As usual the description in the Rosetta page of what is required
is abysmally vague. So it is hard to maintain that an example is
incorrect.

>--
>Ruvim

Groetjes Albert
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Julian Fondren

unread,
Oct 8, 2018, 8:10:23 PM10/8/18
to
On Monday, October 8, 2018 at 4:00:54 PM UTC-5, Ruvim wrote:
> On 2018-10-08 19:52, Julian Fondren wrote:
> > On Monday, October 8, 2018 at 10:53:59 AM UTC-5, Ruvim wrote:
> >> There is a Currying section On Rosetta Code website.
> >> https://rosettacode.org/wiki/Currying
> >>
> >> The most examples there are incorrect. They shows partial application
> >> instead of currying.
> >
> > They do that because partial application is what anyone actually *wants*
> > from currying,
>
> I disagree. There is a special section for Partial application examples:
> https://rosettacode.org/wiki/Partial_function_application
>
> There is no sense to have identical examples in these different sections.
>

This is a rare "argument from RosettaCode taxonomy." :)

But, shouldn't RosettaCode contributors be even more aware of that taxonomy?

And isn't it obvious that all of those examples *could have* demonstrated
a closer approximation of currying, instead?

And yet, they demonstrate partial application. Why? Because nobody would ever
use currying in those languages, even if it's technically possible. Doing it
even in response to strict instructions, and doing it even when there's a
redundant task, is just doing a disservice to the asker. It's like your boss
telling you do something and incidentally making a technology a requirement,
and then you not suggesting that some superior (in your opinion) technology
be used instead.

Perl had a FAQ entry, "how do you implement linked lists in Perl?", and the
correct answer is "you don't because that would be absurd--just use Perl's
arrays", but the FAQ actually contained an absurd implementation of linked
lists. Why? Because the FAQ author figured that the correct answer would be
received as misdirection, intended to hide the dark secret that you can't
write linked lists in Perl.

That RosettaCode contributors are so consistent in this 'error' shows the
strength, not the weakness of my assertion that what people *actually want*
from currying is partial application.

>
> It is just a *programming puzzle*. For fun, for learning, and to
> challenge yourself.
>

You might get more out of https://adventofcode.com/

Ron Aaron

unread,
Oct 9, 2018, 12:05:53 AM10/9/18
to
On 09/10/2018 3:10, Julian Fondren wrote:

> And yet, they demonstrate partial application. Why? Because nobody would ever
> use currying in those languages, even if it's technically possible. Doing it
> even in response to strict instructions, and doing it even when there's a
> redundant task, is just doing a disservice to the asker. It's like your boss
> telling you do something and incidentally making a technology a requirement,
> and then you not suggesting that some superior (in your opinion) technology
> be used instead.

That was exactly what I thought when I read the description of
'currying' vs 'partial application'. I've got PA in 8th, not
theoretical currying, but I call the word 'curry' anyway, since that's
what people actually expect to use.

> Perl had a FAQ entry, "how do you implement linked lists in Perl?", and the
> correct answer is "you don't because that would be absurd--just use Perl's
> arrays", but the FAQ actually contained an absurd implementation of linked
> lists.

Heh. I'll have to include something like that for 8th...

Ruvim

unread,
Oct 9, 2018, 3:09:37 AM10/9/18
to
On 2018-10-09 07:05, Ron Aaron wrote:
> On 09/10/2018 3:10, Julian Fondren wrote:
>
>> And yet, they demonstrate partial application. Why? Because nobody would ever
>> use currying in those languages, even if it's technically possible. Doing it
>> even in response to strict instructions, and doing it even when there's a
>> redundant task, is just doing a disservice to the asker. It's like your boss
>> telling you do something and incidentally making a technology a requirement,
>> and then you not suggesting that some superior (in your opinion) technology
>> be used instead.

It is like your teacher tells you to solve some problem and specially
makes pretentious technology requirements and limitations for education
purpose.


> That was exactly what I thought when I read the description of
> 'currying' vs 'partial application'. I've got PA in 8th, not
> theoretical currying, but I call the word 'curry' anyway, since that's
> what people actually expect to use.

Instead of educating your users, you just indulge their ignorance.


--
Ruvim

Ruvim

unread,
Oct 9, 2018, 3:15:16 AM10/9/18
to
On 2018-10-09 00:24, Albert van der Horst wrote:
> In article <ppfuij$17r6$1...@gioia.aioe.org>,
> Ruvim <ruvim...@gmail.com> wrote:
>> There is a Currying section On Rosetta Code website.
>> https://rosettacode.org/wiki/Currying
>>
>> The most examples there are incorrect. They shows partial application
>> instead of currying.
>
> As usual the description in the Rosetta page of what is required
> is abysmally vague. So it is hard to maintain that an example is
> incorrect.
>

Yes, and issue regarding this problem is pointed in the Discussion:
https://rosettacode.org/wiki/Talk:Currying#What_is_the_task.3F

Also, it refers Wikipedia article that describes the problem in details.

In any case, Rosetta Code issues has nothing to do with the initial
puzzle (how to define "curry" in Forth).


--
Ruvim

Paul Rubin

unread,
Oct 9, 2018, 3:29:41 AM10/9/18
to
Ruvim <ruvim...@gmail.com> writes:
> In any case, Rosetta Code issues has nothing to do with the initial
> puzzle (how to define "curry" in Forth).

The Haskell type signature is

Prelude> :t curry
curry :: ((a, b) -> c) -> a -> b -> c

the closest Forth equivalent I can think of would be something like:

\ : curry ( (xt1: n n -- n ) -- ( xt2: n -- (xt3: n -- n)))

It doesn't seem like a Forthy thing to want to do. But, the new Gforth
closures (see the recent Euroforth papers) might make it easier.

Ruvim

unread,
Oct 9, 2018, 3:48:51 AM10/9/18
to
On 2018-10-09 10:29, Paul Rubin wrote:
> Ruvim <ruvim...@gmail.com> writes:
>> In any case, Rosetta Code issues has nothing to do with the initial
>> puzzle (how to define "curry" in Forth).
>
> The Haskell type signature is
>
> Prelude> :t curry
> curry :: ((a, b) -> c) -> a -> b -> c
>
> the closest Forth equivalent I can think of would be something like:
>
> \ : curry ( (xt1: n n -- n ) -- ( xt2: n -- (xt3: n -- n)))

Yes, I mentioned this one as "curry2" -- that treats a function of two
arguments.


> It doesn't seem like a Forthy thing to want to do. But, the new Gforth
> closures (see the recent Euroforth papers) might make it easier.

Yes, I provide an example of using this syntax in one of the previous
messages.

I don't ask how to solve this puzzle, I just suggest this puzzle if
somebody wants to churn his brain.


--
Ruvim

Ron Aaron

unread,
Oct 9, 2018, 5:44:37 AM10/9/18
to
My job is not to "educate my users". My job is to provide them the
tools they need to accomplish their tasks.

Albert van der Horst

unread,
Oct 9, 2018, 9:46:35 AM10/9/18
to
In article <pphta3$c6j$1...@dont-email.me>,
Well said. Ruvim doesn't seem to understand that language is to
communicate and is living, such that the meaning of terms shift with
usage.

Ruvim

unread,
Oct 9, 2018, 10:11:45 AM10/9/18
to
On 2018-10-09 03:10, Julian Fondren wrote:
> That RosettaCode contributors are so consistent in this 'error' shows the
> strength, not the weakness of my assertion that what people *actually want*
> from currying is partial application.

This error shows that it is just a common misconception.

If you want partial application from currying, you actually need partial
application, not currying.

Regarding the terminology. If we begin to call "partial application" by
"currying", how we will call the original currying? They are definitely
distinct conceptions.

--
Ruvim

franck....@gmail.com

unread,
Oct 9, 2018, 10:42:14 AM10/9/18
to
In Oforth :

Not sure this can be usefull in practice, but, for the puzzle :

: curry3 ( r -- q )
| x y | #[ -> x #[ -> y #[ x y r execute ] ] ] ;

: a swap execute ;

: muladd * + ;

#muladd curry3 2 a 3 a 4 a .
10 ok

Franck

Ruvim

unread,
Oct 9, 2018, 11:06:26 AM10/9/18
to
On 2018-10-09 17:42, franck....@gmail.com wrote:
> Le lundi 8 octobre 2018 17:53:59 UTC+2, Ruvim a écrit :
[...]
>> The *puzzles* are
>>
>> 1. define "curry2" and "curry3" words
>> 2. define "curry3" via "curry2"
>> 3. define general "curry" word ( xt u -- xt1 )
>>
[...]
>>
>> \ testcase for Forth
>>
>> : a ( xt x1 -- x ) swap execute ;
>>
>> : muladd * + ; ' muladd curry3 2 a 3 a 4 a . cr \ prints 10

>
> In Oforth :
>
> Not sure this can be usefull in practice, but, for the puzzle :
>
> : curry3 ( r -- q )
> | x y | #[ -> x #[ -> y #[ x y r execute ] ] ] ;
>
> : a swap execute ;
>
> : muladd * + ;
>
> #muladd curry3 2 a 3 a 4 a .
> 10 ok

Yes. It could even be

#[ * + ] curry3 2 a 3 a 4 a .


And what about points 2 and 3?

I don't know a practical application too, but it just very interesting.


--
Ruvim

franck....@gmail.com

unread,
Oct 9, 2018, 11:26:28 AM10/9/18
to
I don't know if this answer your puzzle but I would write :

: curry2 ( r -- q )
| x | #[ ->x #[ x swap r execute ] ] ;

: curryn \ r n -- q
1- #curry2 times ;

However, parameters have to be inverted...

#muladd 3 curryn 4 a 3 a 2 a .
10 ok

Not a good solution, I will think about another ... :)

Franck

Ruvim

unread,
Oct 9, 2018, 12:39:26 PM10/9/18
to
it should be

: curry2 ( r -- q ) | x | #[ -> x #[ x r execute ] ] ;
: curry3 ( r -- q ) | x y | #[ -> x #[ -> y #[ y x r execute ] ] ] ;


'muladd' is a bad testcase since it does not depend on the order of the
two top arguments.

Something like #[ .s - * ] is far better.



>
> : curryn \ r n -- q
> 1- #curry2 times ;
>
> However, parameters have to be inverted...

Therefore it is called 'rcurry' (from reverse curry). It saves params
from the bottom to the top.

So, you have defined rcurryN via rcurry2. Good step. Now it is needed to
reverse the chain.

Paul Rubin

unread,
Oct 9, 2018, 4:47:44 PM10/9/18
to
Ruvim <ruvim...@gmail.com> writes:
> I don't know a practical application too, but it just very interesting.

Interesting is subjective I guess. For me doing this in Forth only
seems tedious but straightforward. The curry function has to create a
new xt, which in turn creates another new xt that also saves some user
data. So both of these allocate code space and the second allocates a
data cell as well. That means if you're going to use these functions
enough to make creating them worthwhile, you also want to be able to
reclaim the memory, and GC starts looking tempting.

If you find this stuff interesting simply because currying is a cool
concept, that's great, but you might want to try a language (maybe
Scheme if not Haskell) that's more conceptually aligned with it.

Doing stuff like this in Forth is probably better handled using defining
words (if you want just a few curried functions), or OOP in some manner
if you want a lot of them.

Julian Fondren

unread,
Oct 9, 2018, 6:56:38 PM10/9/18
to
On Tuesday, October 9, 2018 at 3:47:44 PM UTC-5, Paul Rubin wrote:
> If you find this stuff interesting simply because currying is a cool
> concept, that's great,

Although it isn't cool at all. It's a bad idea taken an enormous
distance, like everything else in Haskell. Haskell features work like
this:

1. let's defiantly wear a programming hair shirt because we like the
mathiness of it all (currying, laziness, purity)

2. wow this shirt itches. But it has some superficial
utility. Let's write a bunch of blog posts about all the 'cool'
stuff you can do with it.

3. also here's a lifetime's investment into superfluous crap that
makes this shirt not itch quite as much.

You won't see this superfluous crap *anywhere else* people!
(Because nobody else needs it.)

This is unique and amazing Haskell stuff!
(Because nobody else needs it.)

By now I bet your brain is really stretched now, eh?
(Because this was a bad idea to begin with, and now we've a whole
mess of concepts that exist solely to make the idea sorta work.)

4. Here's how you can implement #1 in some non-Haskell language.

Wow, it really sucks in that language!

See how much better Haskell is?

> but you might want to try a language (maybe
> Scheme if not Haskell) that's more conceptually aligned with it.
>
> Doing stuff like this in Forth is probably better handled using defining
> words (if you want just a few curried functions), or OOP in some manner
> if you want a lot of them.

When would this ever actually come up? The answer is: in higher-order
middleware. You have some word W1 that takes an XT, and you are in a
word W2 that also takes an XT, so you want to, within W2, pass your xt
to W1.

: w1 ( xt -- ) s" hello" swap rot execute ; \ a higher-order function

: w2 ( xt -- ) 10 0 do dup w1 loop drop ; \ higher-order 'middleware'

: some-word ( -- )
['] type w2 ;
\ ^-- this won't work: W1 passes ( u c-addr ) to TYPE

It's only when your concern is restricted to w2 that the problem
arises. For whatever reason, W2 must accept a ( c-addr u ) -expecting
xt and it must pass it to a ( u c-addr ) -expecting word.

If you have quotations that can read the enclosing word's locals, then
this is an option:

: w2 {: xt -- :}
[: swap xt execute ;]
10 0 dup w1 loop drop ;

: some-word ( -- )
['] type w2 ;

That's about as pleasant as it gets without heap-allocated and GC'd
definitions, I think. Without looking at return-stack allocations.

Still, I've only run into the W2 problem in Forth when I went looking
for it. Forth has other ways of getting code sharing (note: what W1
gives you is the ability for different XTs to share the s" hello" swap
part of its definition), mainly immediate words, but also outright
loading code again from a block or file.

: x1 s" hello" POSTPONE sliteral POSTPONE swap ; immediate

: x2[
10 POSTPONE literal 0 POSTPONE literal POSTPONE do
POSTPONE x1 POSTPONE swap ; immediate
( ... -- ... )
: ]x2
POSTPONE loop ; immediate

: some-word ( -- )
x2[ type ]x2 ;

some-word \ hellohellohellohellohellohellohellohellohellohello

It's a bit uglier. It has some disadvantages (code bloat). It has some
advantages (better opportunities for optimization). It's templates.

Paul Rubin

unread,
Oct 9, 2018, 8:09:01 PM10/9/18
to
Julian Fondren <julian....@gmail.com> writes:
>> If you find this stuff interesting simply because currying is a cool
>> concept, that's great,
>
> Although it isn't cool at all. It's a bad idea taken an enormous
> distance, like everything else in Haskell. Haskell features work like
> this:

It's certainly not a Haskell idea. It was originally a math/logic idea
going back to the 19th century, and I first learned about it from SICP.

> 1. let's defiantly wear a programming hair shirt because we like the
> mathiness of it all (currying, laziness, purity)

Indeed, but the claim is that the hair shirt was the price of laziness,
and wearing it brought various other benefits even if laziness per se
turns out not to be all that valuable.

https://www.microsoft.com/en-us/research/publication/wearing-hair-shirt-retrospective-haskell-2003/

> You won't see this superfluous crap *anywhere else* people!
> (Because nobody else needs it.)

Eh? Laziness is mostly Haskell-specific these days, but currying is
quite natural once you have function values, and lots of languages
support purity now. There are various things in Haskell that are
painful but its use of pervasive currying just isn't one of them. It's
natural and frequently convenient.

> 4. Here's how you can implement #1 in some non-Haskell language.
> Wow, it really sucks in that language!

(define (curry f)
(lambda (a)
(lambda (b) (f a b))))

seems non-sucky to me.

> Still, I've only run into the W2 problem in Forth when I went looking
> for it.

I'd be interested to know where you found it.

Julian Fondren

unread,
Oct 9, 2018, 8:52:08 PM10/9/18
to
On Tuesday, October 9, 2018 at 7:09:01 PM UTC-5, Paul Rubin wrote:
> Julian Fondren <julian....@gmail.com> writes:
> > 1. let's defiantly wear a programming hair shirt because we like the
> > mathiness of it all (currying, laziness, purity)
>
> Indeed, but the claim is that the hair shirt was the price of laziness,

A: *disparaging description of a thing*

B: yeah but *non-disparaging description of the thing*

> but currying is
> quite natural once you have function values,

It is not natural even in Haskell, which is why Haskell has all these
combinators that do nothing but swap function parameters around.

Look at any other language that happens to have currying, and what you
see is sparse use of it and no library of combinators.

> There are various things in Haskell that are
> painful but its use of pervasive currying just isn't one of them.

Who brought up 'pain'? The point is, it's a bad idea, and the proof is
that when you lean on it you get more complexity, not less.

It's probably really intellectually satisfying to predict planetary
motion in the solar system using epicycles and a heliocentric model.
But it's so satisfying precisely because it's wrong.

>
> > 4. Here's how you can implement #1 in some non-Haskell language.
> > Wow, it really sucks in that language!
>
> (define (curry f)
> (lambda (a)
> (lambda (b) (f a b))))
>
> seems non-sucky to me.

Why? Because it's three lines? Is length of implementation what makes
something sucky or not?

Schemers use stuff like this: http://wiki.call-cc.org/eggref/4/holes

>
> > Still, I've only run into the W2 problem in Forth when I went looking
> > for it.
>
> I'd be interested to know where you found it.

1. hey let's use lots of quotations and higher order functions for no reason

2. <W2 problem>

It's that easy. Go looking for it, and there it is.

1. Hey let's use lots of currying for no reason.

2. <Lots of function combinators>

dxf...@gmail.com

unread,
Oct 9, 2018, 9:50:51 PM10/9/18
to
Same thing, no?

Either one sets out to attract users (which leads to compromise)
- or they come as a consequence of what you've created for yourself.

Paul Rubin

unread,
Oct 9, 2018, 11:06:42 PM10/9/18
to
Julian Fondren <julian....@gmail.com> writes:
> It is not natural even in Haskell, which is why Haskell has all these
> combinators that do nothing but swap function parameters around.

Eh? You can swap the order of elements of a tuple or you can use flip
on a curried function, same thing.

> Look at any other language that happens to have currying, and what you
> see is sparse use of it and no library of combinators.

Combinator libraries are a feature not a bug. If other languages lack
them because they're harder to write in them, then that speaks favorably
of Haskell.

> Who brought up 'pain'? The point is, it's a bad idea, and the proof is
> that when you lean on it you get more complexity, not less.

Add one to all values of a list: map (+1) xs
Is that so much complexity? Versus however you'd write that in
whatever.

> Why? Because it's three lines? Is length of implementation what makes
> something sucky or not?

It's completely straightforward, no mucking around with allocating value
slots etc.

> Schemers use stuff like this: http://wiki.call-cc.org/eggref/4/holes

I guess that is cute but I've never felt the need for it.

> 1. hey let's use lots of quotations and higher order functions for no
> reason
> 2. <W2 problem>
>
> It's that easy. Go looking for it, and there it is.

I don't see much Forth code written like that, except maybe a few
artificial examples on the newsgroup.

What is this issue you have with Haskell? Do you use it? It's not
perfect (nothing is), it deserves criticism in some areas, but the
particular stuff you're hating on works perfectly well in practice.

Ron Aaron

unread,
Oct 10, 2018, 12:22:33 AM10/10/18
to


On 10/10/2018 4:50, dxf...@gmail.com wrote:
> On Tuesday, October 9, 2018 at 8:44:37 PM UTC+11, Ron Aaron wrote:
>> On 09/10/2018 10:09, Ruvim wrote:
>>> On 2018-10-09 07:05, Ron Aaron wrote:
>>
>>>> That was exactly what I thought when I read the description of
>>>> 'currying' vs 'partial application'.  I've got PA in 8th, not
>>>> theoretical currying, but I call the word 'curry' anyway, since that's
>>>> what people actually expect to use.
>>>
>>> Instead of educating your users, you just indulge their ignorance.
>>
>> My job is not to "educate my users". My job is to provide them the
>> tools they need to accomplish their tasks.
>
> Same thing, no?

No, not at all.

> Either one sets out to attract users (which leads to compromise)
> - or they come as a consequence of what you've created for yourself.

I think you're presenting a false-dichotomy there, but in any case
"educating the users" in the sense I and Ruvim were using it is not the
same as "presenting new and useful ideas" to them.

8th (and Forths in general) presents lots of new ideas to users, and so
they need _some_ education in how to best leverage the tool. Some users
grok it, some don't; those who do become very effective at wielding the
tool. The others, mostly never do.

Theoretical currying is not very useful in practice. At least, I've
never run across a situation where I thought it would be beneficial. So
I don't include that particular feature in my product. Nor have any of
my users ever asked for it: which indicates to me that none of them
would find it useful. Were I to get requests from my users, I would
seriously think about adding it, despite my finding it useless.

That's not "compromise", it's responsiveness to a user-base.

Since I'm not interested in battles of intellectual purity, I'm willing
to stray from that purity to make my users' (and my own) lives easier.
That's why 8th is the way it is.

Julian Fondren

unread,
Oct 10, 2018, 12:27:16 AM10/10/18
to
On Tuesday, October 9, 2018 at 10:06:42 PM UTC-5, Paul Rubin wrote:
> Julian Fondren <julian....@gmail.com> writes:
> Combinator libraries are a feature not a bug. If other languages lack
> them because they're harder to write in them, then that speaks favorably
> of Haskell.

All of these thick books about the intricacies of epicycles are a theory,
not a bug. If Newtonian physicists can't find as much to say about their
theory, that speaks favorably of Heliocentrism :)

And what languages are you thinking of, where combinators would be "hard to
write"? A mild challenge? Not "you do exactly the same thing people do in
Haskell"? Forth doesn't have closures, so there's one. What was your second
language?

There's no barrier other than good sense. People in other languages *just
don't do that*. They're not any worse for it. Haskell is the worse for its
community indulging in pointfree programming, in making code more obscure
for no reason at all.

Many languages are hard. Dependent-typed and theorem-proving languages I've
found to be exceptionally difficult. But I don't mind them, because the
difficulty is legitimate, and you get value for your effort. Setting up
constraints in ATS lets you take all kinds of runtime errors and turn them
into compile-time errors. Theorem-proving is what TTD wishes it was: a form
of double-entry bookkeeping for code.

With Haskell, you're always learning shit that only exists for the sake
other parts of Haskell that aren't even any good. It's fake difficulty.
It tugs at your instinct for mastery--to be good at what you do--but the
result is that you're good at a Guitar Hero controller when you wanted to
be good at a guitar.

I resent even the little bit of time I spent on Haskell. If you approach
it without knowing FP or ML or programming at all, you might gain from
by accident. But when you'll be confused when you learn any other language.
Hey, why don't need I need this thing that Haskell made out to be super
important?

> Add one to all values of a list: map (+1) xs
> Is that so much complexity? Versus however you'd write that in
> whatever.

A: this feature is used to make code obscure

B: here's a trivial example of the feature

A: wow! That is indeed trivial! You have opened my eyes!

I already mentioned "languages that incidentally have currying",
like ML. In OCaml:

List.map ((+)1) xs

But you may as well

List.map (fun x -> x + 1) xs

Which is also a thing you could've done in Haskell. Why didn't you?
Rather, if you *accidentally* wrote code like that last example in
Haskell, you would feel compelled to 'fix' it to make it more like
your original example. Do you think that compulsion is healthy?
Is that a good compulsion for programmers to have? Should we call it
virtuous and praise the language that instills it in its programmers?

>
> > Why? Because it's three lines? Is length of implementation what makes
> > something sucky or not?
>
> It's completely straightforward, no mucking around with allocating value
> slots etc.

So, what any language with closures and GC will give you.

Any code that uses it will be sucky, though.

>
> > Schemers use stuff like this: http://wiki.call-cc.org/eggref/4/holes
>
> I guess that is cute but I've never felt the need for it.
>

This is indeed a good reason not to invest in a technology.

The problem with Haskell is that you're tricked. Initially, by cute
examples.

1. wow, that's cute and short. What a terse language.

2. wow, you can do it that way? what a fun thing I am not realizing is
utterly pointless and not just 'pointfree'.

...

10. well this Scheme partial application library is silly but have you
heard of flip? Wow what a useful thing that is. I have all this
knowledge of Haskell now, but weirdly it's never useful when I use
another language.

> I don't see much Forth code written like that, except maybe a few
> artificial examples on the newsgroup.

Like what? Like that POSTPONE heavy code? That's completely normal
stuff to see with data structure code. There's precious little Forth
posted on clf though that isn't a 1000th repost from Hugh.

Anton Ertl

unread,
Oct 10, 2018, 3:43:26 AM10/10/18
to
Julian Fondren <julian....@gmail.com> writes:
>All of these thick books about the intricacies of epicycles are a theory,
>not a bug. If Newtonian physicists can't find as much to say about their
>theory, that speaks favorably of Heliocentrism :)

The Ptolemaic system with the epicycles is called geocentrism, while
the system without epicycles is called heliocentrism; heliocentrism in
modern times is associated with Nicolaus Copernicus, Johannes
Kepler, and Galileo Galilei.

- 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 2018: http://www.euroforth.org/ef18/

Ron Aaron

unread,
Oct 10, 2018, 3:55:39 AM10/10/18
to


On 10/10/18 10:32, Anton Ertl wrote:
> Julian Fondren <julian....@gmail.com> writes:
>> All of these thick books about the intricacies of epicycles are a theory,
>> not a bug. If Newtonian physicists can't find as much to say about their
>> theory, that speaks favorably of Heliocentrism :)
>
> The Ptolemaic system with the epicycles is called geocentrism, while
> the system without epicycles is called heliocentrism; heliocentrism in
> modern times is associated with Nicolaus Copernicus, Johannes
> Kepler, and Galileo Galilei.

Yes. And not to put too fine a point on it, but as a *usable* model,
the geocentric "epicycle" model works quite well for terrestrial
observers. Many traditional calendrical systems are based off it in one
form or another.

Not to mention that the usual heliocentric model isn't 100% correct
either, nor are planetary orbits susceptible to analytic reduction
(because the real world is noisy, &c.). The most current models are
heliocentric with lots of empirically based corrections, and have *many*
terms in the equations.

For example:
https://www.nrel.gov/docs/fy08osti/34302.pdf

Albert van der Horst

unread,
Oct 10, 2018, 4:12:03 AM10/10/18
to
In article <5321d368-1922-4787...@googlegroups.com>,
Julian Fondren <julian....@gmail.com> wrote:
>SNIP>
>
>There's no barrier other than good sense. People in other languages *just
>don't do that*. They're not any worse for it. Haskell is the worse for its
>community indulging in pointfree programming, in making code more obscure
>for no reason at all.

I must take exception to that. I see a lot of solutions to Euler
problems in Haskell that are surprisingly short. That means that
they are surprisingly easy to understand, probably, once you master
Haskell.
>
>Many languages are hard. Dependent-typed and theorem-proving languages I've
>found to be exceptionally difficult. But I don't mind them, because the
>difficulty is legitimate, and you get value for your effort.

This is probably true for Haskell too.

Ruvim

unread,
Oct 10, 2018, 4:52:15 AM10/10/18
to
On 2018-10-10 07:27, Julian Fondren wrote:

> There's no barrier other than good sense. People in other languages *just
> don't do that*. They're not any worse for it. Haskell is the worse for its
> community indulging in pointfree programming, in making code more obscure
> for no reason at all.

Ha. Point-free style is even more essential in Forth than in Haskell.

Do you still programming in Forth?

Julian Fondren

unread,
Oct 10, 2018, 5:04:05 AM10/10/18
to
On Wednesday, October 10, 2018 at 3:12:03 AM UTC-5, Albert van der Horst wrote:
> I must take exception to that. I see a lot of solutions to Euler
> problems in Haskell that are surprisingly short. That means that
> they are surprisingly easy to understand, probably, once you master
> Haskell.

Your speculation says so much more than my experience ever could.

Julian Fondren

unread,
Oct 10, 2018, 5:12:56 AM10/10/18
to
You live right next to a small river. So you go fishing twice a month.
This costs you nothing.

This fellow over here lives in the middle of a desert. So he goes fishing
twice a month. This costs him two-way air fare and a boat rental.

Are you doing the same thing?

Ruvim

unread,
Oct 10, 2018, 5:49:58 AM10/10/18
to
On 2018-10-09 23:47, Paul Rubin wrote:
> Ruvim <ruvim...@gmail.com> writes:
>> I don't know a practical application too, but it just very interesting.

> Interesting is subjective I guess.

Sure.
This puzzle is connected with both Forth and combinatory logic. Also its
solution is simple but difficult to grasp. So it was so interesting for me.


> For me doing this in Forth only
> seems tedious but straightforward. The curry function has to create a
> new xt, which in turn creates another new xt that also saves some user
> data. So both of these allocate code space and the second allocates a
> data cell as well.

Actually, these aspects are trivial. It is easy to define several basic
combinators.

For example, "partial1" combinator can be defined as simple as:

: partial1 ( x xt -- xt1 ) 2>r :noname r> r> lit, compile, postpone ; ;

or even as:

: partial1 ( x xt -- xt2 ) swap p{ lit{} call{} }p ;


The real problem of the puzzle lays in combinatory logic area.


Moreover, it seems that this puzzle can be applied for more classic
languages as well. For example, in ECMAScript (aka JavaScript) curry2
and curry3 can be defined as:

const curry2 = f => a => b => f(a, b);
const curry3 = f => a => b => c => f(a, b, c);

How to define curry3 via curry2? (perhaps, curry2 should be defined in
another way).



> That means if you're going to use these functions
> enough to make creating them worthwhile, you also want to be able to
> reclaim the memory, and GC starts looking tempting.

Not at all — in such scale that will require GC.

> If you find this stuff interesting simply because currying is a cool
> concept, that's great, but you might want to try a language (maybe
> Scheme if not Haskell) that's more conceptually aligned with it.

Funny fact: Haskell language is named after Haskell Curry, who is known
for his work in combinatory logic, that, in general sense, is the
theoretical basis for Forth language (its subset at least).



> Doing stuff like this in Forth is probably better handled using defining
> words (if you want just a few curried functions), or OOP in some manner
> if you want a lot of them.
>

For example, I used partial2 in practice even without all this stuff.
https://github.com/ruv/forth-design-exp/blob/master/lexeme-translator/resolver.api.L2.fth

Creating a chain:

resolver swap ['] combine-either partial2 set-resolver


--
Ruvim

Paul Rubin

unread,
Oct 10, 2018, 4:11:38 PM10/10/18
to
Julian Fondren <julian....@gmail.com> writes:
> And what languages are you thinking of, where combinators would be "hard to
> write"? A mild challenge? Not "you do exactly the same thing people do in
> Haskell"? Forth doesn't have closures, so there's one. What was your second
> language?

Here's an Ocaml version of Parsec:

https://sourceforge.net/projects/pcl-ocaml/

Slides (shows some examples etc.):

https://lprousnth.files.wordpress.com/2007/08/pcl.pdf


You can see that it's considerably uglier than the Haskell version.

> There's no barrier other than good sense. People in other languages *just
> don't do that*. They're not any worse for it.

I'd say look at the Ocaml library above. There are several others like
it. Someone must have tohught it was worthwhile.

> Haskell is the worse for its community indulging in pointfree
> programming, in making code more obscure for no reason at all.

You're all over the place now. What does that have to do with using
currying and having a flip function, instead of using tuple args and
having a function with signature ((a,b) -> c) -> ((b,a) -> c) ?
I'm sure someone has had to write an Ocaml function like that: I've
used such things in Python many times.

Yes of course you can write obscure pointfree code and it's fun for a
while, but then you get over it:

https://www.willamette.edu/~fruehr/haskell/evolution.html

> With Haskell, you're always learning shit that only exists for the
> sake other parts of Haskell that aren't even any good. It's fake
> difficulty.

Maybe there is something to that (look at any of the enumeratee
libraries) but currying isn't an example.

> I already mentioned "languages that incidentally have currying",
> like ML. In OCaml: List.map ((+)1) xs

OK, so OCaml has it as well. Is that new? I didn't know Ocaml had
sections or user-defined infix operators like in the PCL library above,
but just noticed.

> But you may as well List.map (fun x -> x + 1) xs

It's just more fingertyping and clutter, like when Python wants you to
use list comprehensions for everything. It's a matter of taste when to
use a named parameter in functional programming, just like when to use a
variable instead of stack operations in Forth, or inlining a couple of
operations instead of factoring out another named word. Yes you can go
crazy with obscurity but you can also be concise without being obscure.

>> I don't see much Forth code written like that
> Like what? Like that POSTPONE heavy code?

Code that uses xt's a lot, particularly higher order ones.

Mostly I see you grasping around trying to justify some kind of
irrational (or maybe rational but not coherently explained) hatred of
Haskell. Haskell like anything else has significant problems, but the
parts you're bagging on work perfectly well.

Julian Fondren

unread,
Oct 10, 2018, 5:08:21 PM10/10/18
to
On Wednesday, October 10, 2018 at 3:11:38 PM UTC-5, Paul Rubin wrote:
> Julian Fondren <julian....@gmail.com> writes:
> > And what languages are you thinking of, where combinators would be "hard to
> > write"? A mild challenge? Not "you do exactly the same thing people do in
> > Haskell"? Forth doesn't have closures, so there's one. What was your second
> > language?
>
> Here's an Ocaml version of Parsec:

Parser combinators share the word 'combinator' with what I've been talking about.

That is all that they share.

> You can see that it's considerably uglier than the Haskell version.

Which has absolutely nothing to do with "is it hard to do".

> > Haskell is the worse for its community indulging in pointfree
> > programming, in making code more obscure for no reason at all.
>
> You're all over the place now. What does that have to do with using
> currying and having a flip function

You are now asking me what connection a practice that eschews
variables could have with some means by which code can avoid
variables?

> Yes of course you can write obscure pointfree code and it's fun for a
> while, but then you get over it:

They will "get over it" to the extent that they stop saying that they want
to write 'pointfree code'. They will use variables on occasion. But flip's
still part of Haskell's prelude, and typical Haskell code remains obscure.

> Maybe there is something to that (look at any of the enumeratee
> libraries) but currying isn't an example.

The proof is: when you press the idea, you get more complexity. This
complexity has no other purpose but to serve the idea. You would not
want to translate the complexity to a language lacking the idea. Even
a language *with* the idea, but without the culture of abusing it, will
be free of the complexity.

Currying exhibits all of this in Haskell.

> OK, so OCaml has it as well. Is that new?

No.

> It's just more fingertyping and clutter,

Do you type code on a typewriter? Without corrections? You pull the paper
out and then insert it directly into a machine? Can the rest of your code be
found on your walls, framed like art?

If you come back and say, I'd like to do something very slightly different
from what I have here, the "more fingertyping" option easily accommodates
that. The framed-like-art option is thrown away and replaced.

> Code that uses xt's a lot, particularly higher order ones.

Well, of course. Did you manage to also read me as saying otherwise?

> Mostly I see you grasping around trying to justify some kind of
> irrational (or maybe rational but not coherently explained) hatred of
> Haskell.

Take a look at this: https://duckduckgo.com/?q=the+hot+water+challenge

It's like the ice water challenge, but with boiling water. You take some water,
bring it to a boil, and then throw it on your face.

This is a concrete and very easy to understand example, so I'm sure that
you can think of very effective ways to persuade someone to not do this,
who is so incredibly clueless as to even consider it. But what I'd like to point
out is that there are also many ineffective ways to persuade such a person.
The ineffective ways would use words like 'hot', and 'hurt', and 'damage',
("yeah that's the point", "ice water hurts too", "I'll do it quick so it won't
damage me so much!"). When someone simply lacks an important concept,
it doesn't help to try and appeal to their understanding of that concept.

The usual rhetoric about programming languages is that simply learning
them makes you a better programmer. This is not true of Haskell. Learning
Haskell *only* makes you a better Haskell programmer. What you gain is
non-transferrable. I've said this in many ways already, and you've read what
I've said, presumably, but now you can't even say for sure that you know why
I dislike Haskell. Even before you can agree or disagree with my position or
not, you're not even sure if I *have* a position. This is hot water challenge
levels of communication difficulty. And I can't just find a hot stove for you
to touch. Suppose I gave you a non-transferrable amount of money. Would
you be pleased?

To answer the obvious objection: Haskell borrows from a lot of other
languages and if you learn it without knowing those other languages then
you can accidentally benefit a great deal from it. ML in particular is such
a work of genius that you'll get some pleasure from its presence no matter
how many layers of distracting fat are arranged around it.

hughag...@gmail.com

unread,
Oct 10, 2018, 10:26:07 PM10/10/18
to
On Tuesday, October 9, 2018 at 5:52:08 PM UTC-7, Julian Fondren wrote:
> It's probably really intellectually satisfying to predict planetary
> motion in the solar system using epicycles and a heliocentric model.
> But it's so satisfying precisely because it's wrong.

Julien Fondren has the terms "heliocentric" and "geocentric" backwards --- this is all pseudo-intellectual nonsense, as usual.

NN

unread,
Oct 11, 2018, 6:28:11 AM10/11/18
to
> The *puzzles* are
>
> 1. define "curry2" and "curry3" words
> 2. define "curry3" via "curry2"
> 3. define general "curry" word ( xt u -- xt1 )
>
>
>
> In Haskell any function is already curried.
> Conceptually (not using Haskell syntax):
> f(a)(b) === f(a,b)
> f(a)(b)(c) === f(a,b,c)
>
> In ECMAScript:
> let curry2 = f => a => b => f(a, b);
> let curry3 = f => a => b => c => f(a, b, c);
>
> curry3( (a,b,c) => (a*b+c) )(2)(3)(4) === 10
>
>
>
> \ testcase for Forth
>
> : a ( xt x1 -- x ) swap execute ;
>
> : muladd * + ; ' muladd curry3 2 a 3 a 4 a . cr \ prints 10
>
>
> --
> Ruvim

Hi Ruvim,

I am a little late to this discussion, so I will try not to reiterate points already made.

I have used the terms interchangeably without realising they are different.
Your post made me look it up. From Wikipedia,

Currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument.

Partial application refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.

Seems to me that the only difference is that currying involves converting multiple arguments to sequence of functions that take a single argument only.
Whereas partial just reduces the arity. (So you may have a sequence of functions with one or more args).

Would you agree this is the ( only ?) difference ?

NN

Ruvim

unread,
Oct 11, 2018, 7:36:00 AM10/11/18
to
On 2018-10-11 13:28, NN wrote:
> On Monday, 8 October 2018 16:53:59 UTC+1, Ruvim wrote:
[...]
>>
>> The *puzzles* are
>>
>> 1. define "curry2" and "curry3" words
>> 2. define "curry3" via "curry2"
>> 3. define general "curry" word ( xt u -- xt1 )
>>
>>
>>
>> In Haskell any function is already curried.
>> Conceptually (not using Haskell syntax):
>> f(a)(b) === f(a,b)
>> f(a)(b)(c) === f(a,b,c)
>>
>> In ECMAScript:
>> let curry2 = f => a => b => f(a, b);
>> let curry3 = f => a => b => c => f(a, b, c);
>>
>> curry3( (a,b,c) => (a*b+c) )(2)(3)(4) === 10
>>
>>

>
> Hi Ruvim,
>
> I am a little late to this discussion, so I will try not to reiterate points already made.

Sure, it is not late. And still nobody has suggested a solution for the
initial puzzle ;)


>
> I have used the terms interchangeably without realising they are different.
> Your post made me look it up. From Wikipedia,
>
> Currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument.

NB: "sequence" is in the sense that each function (except the last one)
returns the next function.


>
> Partial application refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.
>
> Seems to me that the only difference is that currying involves converting multiple arguments to sequence of functions that take a single argument only.

It does not convert a function into sequence of functions. Just have a
look into usage examples.


> Whereas partial just reduces the arity.

Yes, partial application reduces the arity by mean of fixing some arguments.


> (So you may have a sequence of functions with one or more args).
>
> Would you agree this is the ( only ?) difference ?

If we talk about differences, we should also take into account similarities.

What are their similarities? They both produces a function as the
result. I don't see other formal similarities.

Also, "partial" can be defined via "curry", and vice versa — "curry" can
be defined via "partial". Hence, they also have the same kind of some
internal substance, but I can't intelligibly describe it.

Figuratively speaking, "partial" reduces arity in the space, but "curry"
transforms arity in the space into arity in the time.


--
Ruvim

NN

unread,
Oct 11, 2018, 8:49:55 AM10/11/18
to
The fault is probably mine, but I am still confused.

If i suggested this :

Currying
========

eg f(a,b,c,d,e) = n

g= f(a)
h= g(b)
i = h(c)
j = i(d)
n= j(e)

or

g= f(c)
h= g(e)
i = h(a)
j = i(d)
n= j(b)



Partial application
===================
f(a,b,c,d,e) = n

g=f(a,c,e)
n=g(b,d)

or

g=f(a,b)
h=g(d,e)
n=h(c)

or some other combination...

where f,g,h,i,j are functions & n = value

Is this a better way of visualing it?

NN

Julian Fondren

unread,
Oct 11, 2018, 9:07:53 AM10/11/18
to
On Thursday, October 11, 2018 at 7:49:55 AM UTC-5, NN wrote:
> The fault is probably mine, but I am still confused.
>
> Currying
> ========

Variable-arity functions do not exist.

You have functions of one argument, only.

To simulate higher arities, you have your function return
a function that takes the next parameter.

> Partial application
> ===================

You want to supply some parameters to a function, to get
the result of a new function that accepts the remaining
parameters.


When people look at languages that have curried functions
and then try and work with them in other languages, they
tend to come up with a degenerate form of partial application
that supplies a function's first argument, only.

Ruvim

unread,
Oct 11, 2018, 10:15:00 AM10/11/18
to
On 2018-10-11 15:49, NN wrote:
> On Thursday, 11 October 2018 12:36:00 UTC+1, Ruvim wrote:
>> [...] >>
>> If we talk about differences, we should also take into account similarities.
>>
>> What are their similarities? They both produces a function as the
>> result. I don't see other formal similarities.
>>
>> Also, "partial" can be defined via "curry", and vice versa — "curry" can
>> be defined via "partial". Hence, they also have the same kind of some
>> internal substance, but I can't intelligibly describe it.
>>
>> Figuratively speaking, "partial" reduces arity in the space, but "curry"
>> transforms arity in the space into arity in the time.


>
> The fault is probably mine, but I am still confused.
>
> If i suggested this :
>
> Currying
> ========
>
> eg f(a,b,c,d,e) = n
>
> g= f(a)
> h= g(b)
> i = h(c)
> j = i(d)
> n= j(e)

It is not a currying per se, it is an example of a system that supports
currying implicitly (under the hood). So, you don't have to use explicit
"curry" function.

In a system that does not support currying implicitly it would be:

f0(a,b,c,d,e) === n
f = curry(f0)

g = f(a)
h = g(b)
i = h(c)
j = i(d)
n = j(e)


>
> or
>
> g= f(c)
> h= g(e)
> i = h(a)
> j = i(d)
> n= j(b)

These g,h,i,j,n are not the same as in the example above.
It can be correctly expressed as:

g1 = f(c)
h1 = g1(e)
i1 = h1(a)
j1 = i1(d)
n1 = j1(b)

where
n1 === f0(c,e,a,d,b)



>
>
>
> Partial application
> ===================
> f(a,b,c,d,e) = n
>
> g=f(a,c,e)
> n=g(b,d)

In a system that supports currying implicitly,

f(a,b,c,d,e) === f(a)(b)(c)(d)(e)

g=f(a,c,e) === f(a)(c)(e)

g(b,d) === g(b)(d) === f(a)(c)(e)(b)(d) === f(a,c,e,b,d)
(that is not equal to n above in general case)

g is partially applied f.

So, in such systems when you pass into a function not all arguments, you
get partially applied function as the result.



In a system that does not support currying implicitly it would be

g = partial(f,a,c,e)

g(b,d) === f(a,c,e,b,d)



[...]
>
> where f,g,h,i,j are functions & n = value
>
> Is this a better way of visualing it?
>

--
Ruvim

NN

unread,
Oct 12, 2018, 9:12:18 PM10/12/18
to
Hi Ruvim ,

Order of args is important. Here's what I came up with for curry2 and curry3.

Would be acceptable as a forth solution?

\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
Marker testcode1.f

: @- dup @ swap cell- ;

: curry2 ( xt <name> -- )
create , 0 , 0 ,
does> cell+ !
does> dup >r cell+ cell+ !
r> 2 cells + @- @- @- drop execute ;

: f1 ( y x -- z )
/ ;
' f1 curry2 f0

5 f0
10 f0 cr . cr

: curry3 ( xt <name> -- )
create , 0 , 0 , 0 ,
does> cell+ !
does> cell+ cell+ !
does> dup >r 3 cells + !
r> 3 cells + @- @- @- @- drop execute ;

: g1 ( z y x -- w )
- * ;

' g1 curry3 g0

5 g0
7 g0
9 g0 cr . cr

\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


NN

Ruvim

unread,
Oct 13, 2018, 5:51:01 AM10/13/18
to
On 2018-10-13 04:12, NN wrote:
> Hi Ruvim ,
>
> Order of args is important. Here's what I came up with for curry2 and curry3.
>
> Would be acceptable as a forth solution?

This solution does not fit the formal criteria — the stack effects of
the certain words.

Also, the real challenge is the points 2 and 3 — define curry3 via
curry2 and define curry that treats function of any arity.


>
> \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
> Marker testcode1.f
>
> : @- dup @ swap cell- ;
>
> : curry2 ( xt <name> -- )
> create , 0 , 0 ,
> does> cell+ !
> does> dup >r cell+ cell+ !
> r> 2 cells + @- @- @- drop execute ;
>
> : f1 ( y x -- z )
> / ;
> ' f1 curry2 f0
>
> 5 f0
> 10 f0 cr . cr
>
> : curry3 ( xt <name> -- )
> create , 0 , 0 , 0 ,
> does> cell+ !
> does> cell+ cell+ !
> does> dup >r 3 cells + !
> r> 3 cells + @- @- @- @- drop execute ;
>
> : g1 ( z y x -- w )
> - * ;
>
> ' g1 curry3 g0
>
> 5 g0
> 7 g0
> 9 g0 cr . cr
>
> \ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


f0 and g0 are a kind of generators. But on each step they consume a
parameter in place of produce a parameter (as usual generators).


Also they misuses 'does>'
Since 'does>' appends the run-time semantics to the current (last)
definition.

' f1 curry2 f0
' g1 curry3 g0

5 f0 \ will change g0
3 g0 \



--
Ruvim

Ruvim

unread,
Oct 13, 2018, 6:33:47 AM10/13/18
to
It was wrong citation.

I meant the following:
"Run-time: Replace the execution semantics of the most recent
definition" https://forth-standard.org/standard/core/DOES

So, when one 'does>' performs another 'does>' — it affects the last
definition only.


> ' f1 curry2 f0
> ' g1 curry3 g0
>
> 5 f0 \ will change g0
> 3 g0 \


So, it is just simpler to rely on ':noname'

--
Ruvim

Ruvim

unread,
Oct 16, 2018, 12:38:11 AM10/16/18
to
[spoiler warning]
> The *puzzles* are
>
> 1. define "curry2" and "curry3" words
> 2. define "curry3" via "curry2"
> 3. define general "curry" word ( xt u -- xt1 )
>
>
>
> In Haskell any function is already curried.
> Conceptually (not using Haskell syntax):
>   f(a)(b) === f(a,b)
>   f(a)(b)(c) === f(a,b,c)
>
> In ECMAScript:
>   let curry2 = f => a => b => f(a, b);
>   let curry3 = f => a => b => c => f(a, b, c);
>
>   curry3( (a,b,c) => (a*b+c)  )(2)(3)(4) === 10
>
>
>
> \ testcase for Forth
>
> : a ( xt x1 -- x ) swap execute ;
>
> : muladd * + ; ' muladd curry3  2 a 3 a 4 a . cr \ prints 10
>


One of the possible solution is following.


\ some helpers
: =? ( x x1 -- true | x false ) over = dup if nip then ;
: ?E ( flag -- ) postpone if postpone exit postpone then ; immediate


\ constant
: k ( x -- xt ) p{ lit{} }p ;

\ composition of two functions
: co ( xt2 xt1 -- xt ) p{ call{} call{} }p ;

\ The two above words can be easily defined in bare ANS Forth
\ via ':noname' but just slightly longer.
\ In their turn, p{ ... }p are defined in ANS Forth too.


\ reverse composition of two functions
: rco ( xt1 xt2 -- xt ) swap co ;

: partial1 ( 1*x xt -- xt1 ) swap k co ;

: curry2 ( xt -- xt1 ) 'partial1 partial1 ;

: curry3 ( xt -- xt1 ) curry2 'curry2 rco ;
: curry4 ( xt -- xt1 ) curry2 'curry3 rco ;
: curry5 ( xt -- xt1 ) curry2 'curry4 rco ;

: curry ( xt u -- xt1 ) 1- 0 =? ?E itself partial1 swap curry2 co ;


\ 'itself' cannot be defined in ANS Forth.
\ A possible workaround:

: partial2 ( 2*x xt -- xt1 ) partial1 partial1 ;

: curry ( xt u -- xt1 )
p{ ( xt it u -- xt1 )
1- 0 =? if drop exit then
over partial2 swap curry2 co
}p tuck execute \ a kind of Y combinator
;

\ Another workaround:

0 VALUE xt-of-curry
: curry ( xt u -- xt1 ) 1- 0 =? ?E xt-of-curry partial1 swap curry2 co ;
' curry TO xt-of-curry





=== *How to find this solution*

== *A brute force solution* for puzzle 1

: curry2
p{
p{ lit{} call{ lit{} }call }p
}p
;
: curry3
p{ p{
p{ lit{} lit{ lit{} }lit call{ lit{ lit{} }lit }call }p
}p }p
;

\ these definitions can be tested
\ using the following Forth extension:
\
https://github.com/ruv/forth-design-exp/blob/master/lexeme-translator/advanced.example.fth


== *Simplification* of curry2

The above definition of curry2 is equivalent to (⇔)
\ (1.1)
: curry2
p{
lit{} swap p{ lit{} call{} }p
}p
;

⇔ \ (1.2) factor out partial1 function

: partial1 ( x xt -- xt1 ) swap p{ lit{} call{} }p ;

: curry2 p{ lit{} partial1 }p ;

⇔ \ (1.3) lift up partial1

: curry2 'partial1 swap p{ lit{} call{} }p ;

⇔ \ (1.4) substitute partial1 in place of its body

: curry2 'partial1 partial1 ;



partial1 can be also expressed via more fundamental combinators

\ constant
: k ( x -- xt ) p{ lit{} }p ;

\ composition of two functions
: co ( xt2 xt1 -- xt ) p{ call{} call{} }p ;

: partial1 ( x xt -- xt1 ) swap k co ;



== *Simplification* of curry3

: curry3
p{ p{
p{ lit{} lit{ lit{} }lit call{ lit{ lit{} }lit }call }p
\ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
\ this fragment is almost curry2 core in deeper nesting
}p }p
;
⇔ \ (2.1) factor it out into separate anonymous function
: curry3
p{
p{
p{ lit{ lit{} }lit call{ lit{ lit{} }lit }call }p
swap
p{ lit{} call{} }p
}p
}p
;
⇔ \ (2.2) lift it up
: curry3
p{
p{ lit{} call{ lit{} }call }p

p{
lit{}
swap
p{ lit{} call{} }p
}p

}p
;
⇔ \ (2.3) factor it out into separate anonymous function again,
\ refactor the second part (see also (1.1) )
: curry3
p{
p{ lit{} call{ lit{} }call }p
}p

p{
call{}

p{
p{ lit{} call{ lit{} }call }p
}p

}p
;
⇔ \ (2.4) substitute curry2 in place of its body
: curry3
curry2
p{
call{}
curry2
}p
;
⇔ \ (2.5) lift up the second curry2
: curry3
curry2 'curry2
swap p{ call{} call{} }p
;
⇔ \ (2.6) use rco combinator
: curry3 curry2 'curry2 rco ;


And we have got a simple solution for puzzle 2.



Puzzle 3 is solved via generalization of definitions for curry3, curry4,
curry5.


=== *The solution in bare ANS Forth*

: k ( x -- xt ) >r :noname r> postpone literal postpone ; ;
: co ( xt2 xt1 -- xt ) 2>r :noname 2r> compile, compile, postpone ; ;
: partial1 ( 1*x xt -- xt1 ) swap k co ;
: curry2 ( xt -- xt1 ) ['] partial1 partial1 ;
: curry3 ( xt -- xt1 ) curry2 ['] curry2 swap co ;
0 VALUE xt-of-curry
: curry ( xt u -- xt1 )
1- ?dup 0= if exit then
xt-of-curry partial1 swap curry2 co
; ' curry TO xt-of-curry


\ 10 lines. With 'itself' word it could be 6 lines!



\ testcase

: execute-balance ( i*x xt -- j*x n ) depth 1- >r execute depth r> - ;
: execute-balanced ( i*x xt n -- j*x ) \ j = i + n
>r execute-balance r> = if exit then -11 throw \ result out of range
;
: a ( xt x -- x1 ) swap 0 execute-balanced ;

:noname cr .s cr - ; curry2 3 a 4 a . cr \ 1
:noname cr .s cr - * ; curry3 3 a 4 a 5 a . cr \ 5
:noname cr .s cr - * ; 3 curry 3 a 4 a 5 a . cr \ 5
:noname cr .s cr - * + ; 4 curry 1 a 2 a 3 a 4 a . cr \ 7
:noname cr .s cr - + * - ; 5 curry 1 a 2 a 3 a 4 a 5 a . cr \ -11


--
Ruvim

Bob Armstrong

unread,
Oct 25, 2018, 12:07:13 AM10/25/18
to
> --
> Ruvim

I don't think of them as such but I guess they sort of are kinda Currying .
Various ` operators , to use the APL term are central to CoSy's utility in applying functions to its lists .

Most fundamentally are 4 ` each operators :

R { fn } 'm | apply fn to each item in R

L R { fn } 'd | apply fn between each corresponding pair of items of lists L and R ( modulo indexing of shorter to match longer )

L R { fn } 'L | apply fn between each item of L and R , essentially Currying R

L R { fn } 'R | apply fn between L and each item of R , essentially Currying L


'm for monadic = taking a single argument ; 'd for dyadic = taking 2 arguments .

--
Let me also point out an aspect of Arthur Whitney's K , from which CoSy evolves . The essential syntax for application of a function is

f[ x0 ; x1 ; x2 ; ... ]

If any of the parameter slots are empty , eg:

f[ x0 ; ; x2 ; ... ]

the function Currys the presented parameters , eg: x0 and x2 , and returns the function "projected" , to use the K terminology , on to the space of the absent parameters . Thus ,

f[ x0 ; ; x2 ; ... ] x1

would be equivalent to the first expression .


Ruvim

unread,
Oct 27, 2018, 6:09:43 PM10/27/18
to
On 2018-10-25 07:07, Bob Armstrong wrote:
> On Monday, October 8, 2018 at 9:53:59 AM UTC-6, Ruvim wrote:
[...]
[...]

> I don't think of them as such but I guess they sort of are kinda Currying .
> Various ` operators , to use the APL term are central > to CoSy's utility in applying functions to its lists .
>
> Most fundamentally are 4 ` each operators :
>
> R { fn } 'm | apply fn to each item in R
>
> L R { fn } 'd | apply fn between each corresponding pair of items of lists L and R ( modulo indexing of shorter to match longer )
>
> L R { fn } 'L | apply fn between each item of L and R , essentially Currying R
>
> L R { fn } 'R | apply fn between L and each item of R , essentially Currying L
>
>
> 'm for monadic = taking a single argument ; 'd for dyadic = taking 2 arguments .

> --
> Let me also point out an aspect of Arthur Whitney's K , from which CoSy evolves . > The essential syntax for application of a function is
>
> f[ x0 ; x1 ; x2 ; ... ]
>
> If any of the parameter slots are empty , eg:
>
> f[ x0 ; ; x2 ; ... ]
>
> the function Currys the presented parameters , eg: x0 and x2 , and returns the function "projected" , to use the K terminology , on to the space of the absent parameters . Thus ,
>
> f[ x0 ; ; x2 ; ... ] x1
>
> would be equivalent to the first expression .
>

It is a partial application: when you pass not all arguments you get in
the result another function (that will take some or all the rest arguments).

f1: { (x - y) * z }
f2: f1[;3]
f2[5;4]

output: 8

It works out of the box like in Haskell.

Don't see that the mentioned operators of CoSy return a function
https://github.com/CoSyBob/CoSy/blob/4thCoSy/CoSy/CoSy.f


Just if somebody interested, "K" language implementation:
https://github.com/kevinlawler/kona

--
Ruvim

Bob Armstrong

unread,
Oct 28, 2018, 12:22:06 AM10/28/18
to
As with a number of things I don't see what the big deal is given RPN .

It's true that unlike K and J and at least Dyalog APL one can't write the equivalent of , in K ,

Q : 5 *
Q 5

25

but that seems a minor advantage over

: Q 5 * ;

Certainly I often make functions with incorporated parameters .

What I actually would like to get away from in Forth is the inclusion of the name in the definition of the function .
0 new messages