At 03:08 PM 12/6/99 +0100, you wrote:
>Hi, Elan,
>
>Your curried doesn't really work.

My curried *really* works. Granted, it's a template, a baby version of a
curried function. It was only meant to illustrate to Russ what currying is
about. But, it *really* works, which cannot be said of the curried function
you included in your email. 

My curried function's shortcomings are with respect to its intelligence in
identifying REBOL language constructs, i.e. its parsing abilities are
limited and can be easily extended using REBOL's word parse, but with
respect to the principal statements you make about REBOL's supposed
function reentry limitations and garbage collection problems, my function
demonstrates that the feature set provided by REBOL easily permits the
implementation of a curried function.

To emphasize it, Ladislav, my curried function does do its job within the
limited context I intended for it, and I documented as much in my last
message. Perhaps you so rapidly rush to complain because this version of
curried readily demonstrates that you are mistaken when you claim that it
is because of

- REBOL's garbage collector bug, and/or because of
- a supposed inability of REBOL functions to return functions

that implementating a full-scale curried function is impossible. My curried
function should not work properly on add and not generate a correct addc,
which is even capable of generating brandnew functions, such as add4 and
add5, with all functions, including their arguments, properly independent
of each other, if garbage collector bug and/or function return limitations
were to be blamed, as you suggested in an earlier message to Russ.

Both my implementation of addc as well as my implementation of curried
demonstrate that neither the garbage collection bug, nor the supposition
that REBOL suffers some limitations on returning functions or function
reentrancy problems are to be blamed.

I expected your response. The question is, do you want to think along, or
are you *just* looking to complain. In the message you quote I introduce
the curried function by saying:

>In the case of add this is extremely trivial to implement. This is a quick
>and dirty example of the implementation of a function that generates
>curried functions. Note that for more complicated REBOL functions we would
>need a more intelligent parser to parse REBOL code, or a smarter way of
>identifying the symbols we want to replace, while we leave the rest of the
>code untouched.

To guide your searching eyes to the relevant passage: In the above quote it
begins with "Note that for more complicated REBOL functions we need a more
intelligent parser..."

So, you managed to find a function that curried does not handly properly?
I'll let you know that this early version of curried is incapably of
handling most REBOL functions properly. Note that the current version of my
curried function even handles the following properly:

>> add4+5: addc add4 5
>> add4+5 9
== 18

Having created a function add4 I can submit this function to the curry
function addc together with a new argument to be bound. The newly created
function is capable of processing additional arguments. And again:

>> add4+5+9: addc add4+5 9
>> add4+5+9 9
== 27

Yes, this is a work under development and it will not do its job correctly
for most functions you can implement under REBOL. It was included here not
as an early alpha but only as an illustration for what I'm trying to
explain here to Russ. My curried function does however correctly convert
add to addc, which in turn is capable of converting add 4 to add4, add4 + 5
to add4+5 and so on. It should also correctly convert a minus to minusc, or
multiply to a multiplyc function in a way that is generic enough that it
can work with other REBOL functions as well, once it has been extended. In
its current incarnation it may also work with some other simple functions.
It will not convert REBOL natives.

I think if you take a minute to see how simple the implementation of the
curried function really is, something that would be far more complicated to
implement in something like Perl, I trust (any Perl experts out there?),
you should slowly begin to realize that it is well worth your while to
begin to learn some of the more subtle details of REBOL programming.

I had not documented that my curried function is immune to garbage
collection problems. Here is the test:

>> add: func [x y] [ x + y ]
>> addc: curried :add
>> add4: addc 4
>> add5: addc 5
>> add4 4
== 8
>> add5 4
== 9
>> recycle                               <===== Here I recycle
>> add4 4
== 8
>> add5 4
== 9

Garbage collection poses no problem for my curried version.

Now, I tried the version of curried you included in your message and 

a) I think it is not designed properly
b) Because your curried function creates functions that rely on the
argument in curried itself, your curried function will fail, when you try
to create intermediate curried functions that bind the first argument and
process the remaining arguments, as soon as curried is used to create one
or more additional curried functions (see our thread under function
reentry, where this has been discussed to the point where you report that
you have understood the underlying mechanism).

ad a) I believe your curried function is not designed properly in that it
requires a function and in addition a block of words. Since the function
you pass to curried must meet the formal REBOL requirements for a function,
it includes the block of arguments already. Why do require that they
passed? My curried function uses the function's own argument block and does
not require that the argument block be passed separately. Why don't you
look at how I did that and modify your curried function accordingly?

I am not sure what your curried function is supposed to return. It most
certainly does not return a function that will generate new functions,
whose first argument has been bound to a value passed to that function.
That is what a curried function should do. Mine does do that. Why don't you
take a look at how I did that and use a similar approach?

ad b) We had established in the function reentry thread that relying on the
argument binding in the parent function is dangerous, when you reuse the
parent function to create additional descendant functions. 
I tested your curried function with add and this is what I get:

>> ladislav-addc: ladislav-curried :add [x y] ;- shouldn't need to add block
>> source ladislav-addc
ladislav-addc: func [x y][func [] [function x y]]
>> ladislav-add4: ladislav-addc 4
** Script Error: ladislav-addc is missing its y argument.
** Where: ladislav-add4: ladislav-addc 4

Ladislav-addc should not require a y argument, since it is supposed to
create a curried function, in other words a function that binds the first
argument and will in future use the bound argument together with a second
or more arguments.

Contrast the above code with my version, which at least does handle add
correctly:

>> add: func [x y] [ x + y ]
>> addc: curried :add
>> add4: addc 4
>> add5: addc 5
>> add4 4
== 8
>> add5 4
== 9

Study it and learn from it. Why not?

>
>e.g. use this:
>
>f: func [x y] [reduce [x y]]
>
>
>But, try the following:
>
>recycle/off ;at least the faulty GC will not mess things...
>
>curried: func [
>    "Create curried functions" [catch]
>    fnc [any-function!] "Function to be curried"
>    args [block!] "Arguments of the curried fnc"
>    /local formargs nonargs arguments body
>    ] [
>    formargs: first :fnc
>    if not empty? nonargs: difference/only args formargs [
>        throw make error! compose/deep [
>            script expect-set [(formargs)] [(nonargs)]
>            ]
>        ]
>    do func [[throw] /local function] [
>        function: :fnc
>        arguments: difference/only formargs args
>        body: compose [function (formargs)]
>        return func args reduce ['func arguments body]
>        ]
>    ]
>
>>> f: func [x y] [reduce [x y]]
>>> g: func [x y] [reduce [y x]]
>>> f1: curried :f [x]
>>> do f1 1 2
>== [1 2]
>>> f2: curried :g [x]
>>> do f2 3 4
>== [4 3]
>>> do f1 1 2
>== [1 2]
>
>Try your curried for yourself.

Why? Why "for yourself"? 

Elan



I had written:
>
>Hi Russ,
>you wrote:
>>What does "curried" mean in the context used here?
>
>In "Introduction to Functional Programming using Haskell", Richard Bird
>explains currying like this (p. 12):
>
>"A useful device for reducing the number of parentheses in an expression is
>the idea of replacing a structured argument by a sequence of simpler ones
>..."
>
>Next Bird presents the function smaller which takes two arguments, x and y,
>and returns the smaller of the two. He then presents a curried version of
>smaller that he calls smallerc. He describes this curried version like this:
>
>"More precisely, smallerc is a function that takes an integer x as argument
>and returns a function smallerc x; the function function smallerc x takes
>an integer y as argument and returns an integer; namely the smaller of x
>and y."
>
>Bird then goes on to demonstrate the same idea using add and its curried
>version addc.
>
>Let's look at it in REBOL terms:
>
>add: func [x y] [x + y]
>
>This would be a non-curried add function. We'll call the curried version
>addc. Let's describe what addc will have to do:
>
>When addc is supplied with an argument, it will have to return a function,
>which, when it in turn is supplied with an argument, will add the argument
>that was supplied to addc to the argument supplied to the function addc
>returned.
>
>Let's assume we've somehow managed to implement addc. Then we could define
>a function add4, which expects one argument and adds 4 to that argument,
>like this:
>
>add4: addc 4
>
>We could create another function, add5, which always adds 5 to the argument
>it is passed, like this:
>
>add5: addc 5
>
>Then, when we say
>
>add4 3
>
>REBOL should return 7. When we say add5 3 REBOL should return 8. It's
>trivial to implement addc:
>
>>> addc: func [x] [ func [y] compose [ (x) + y ] ]
>
>Let's test it:
>
>>> add4: addc 4
>>> add5: addc 5
>>> add4 3
>== 7
>>> add5 3
>== 8
>>> add4 3
>== 7
>
>Let's look at add4:
>>> source add4
>add4: func [y][4 + y]
>
>and add5 looks like this:
>>> source add5
>add5: func [y][5 + y]
>
>It would be nice to define a function curried that would allow us to
>automate the process of transforming a non-curried function into a curried
>function.
>
>Instead of implementing addc manually, we could say to REBOL
>
>addc: curried add
>
>and - given that add was defined as add: func [x y] [ x + y ], curried
>would return the function addc that we implemented manually above.
>
>Well, it can be done quite easily. What does curried need to do?
>
>1. It has to accept a function as an argument. That function will be
>transformed into a curried function.
>2. The curried function will expect the same amount of arguments as the
>non-curried function.
>3. The curried function will return a function in which the first argument
>has been dereferenced and the remaining arguments are supplied as the
>curried functions argument block.
>
>Example:
>
>Given add: func [x y] [ x + y ]
>
>addc: curried add
>
>addc should now evaluate to
>
>addc: [x y] [ func [y] [compose [(x) + y] ]
>
>In the case of add this is extremely trivial to implement. This is a quick
>and dirty example of the implementation of a function that generates
>curried functions. Note that for more complicated REBOL functions we would
>need a more intelligent parser to parse REBOL code, or a smarter way of
>identifying the symbols we want to replace, while we leave the rest of the
>code untouched.
>
>
>curried: func [ f [any-function!] /local new-body first-arg func-args
>                                         func-spec spec body]
>
>  body: make block! []
>  func-spec: make block! []
>  first-arg: first first :f
>  foreach symbol second :f [
>    either symbol = first-arg [
>      append body compose [(make paren! reduce [symbol])]
>    ][
>      append body symbol
>    ]
>  ]
>  spec: copy/part first :f 1
>  insert/only func-spec body
>  insert func-spec 'compose
>  insert/only func-spec func-args: at first :f 2
>  insert func-spec 'func
>  return func spec func-spec
>]
>
>Now let's put curried to work:
>
>>> addc: curried :add
>>> source addc
>addc: func [x][func [y] compose [(x) + y]]
>>> add1: addc 1
>>> add10: addc 10
>>> add1 1
>== 2
>>> add10 10
>== 20
>>> add1 5
>== 6
>>> add10 5
>== 15
>>> add2: addc 2
>>> add2 1
>== 3
>>> add1 2
>== 3
>>> add10 -7
>== 3
>
>Please report all bugs, other annoyances or improvements. If I have time, I
>will do some more work on this function.
>
>
>Hope this helps,
>
>Elan
>
>
>
>
>

Reply via email to