Hi, Elan,

Your curried doesn't really work.

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.

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