Search code examples
schemelisp

How to create sum function for all items in list using reduce and curry in Scheme?


I'm playing with Scheme I want to run something like this:

(reduce curry + '(1 2 3))

why this don't work? I also tried to run this:

((curry + 1) 2)
((curry (curry + 1) 2) 3)
((curry (curry (curry + 1) 2) 3) 4)

In LIPS my Scheme implementation it works (see beta version REPL). Why this don't work in Kawa or Guile. Is my implementation not correct? I don't have arity checks on function calls. The function is always called, is this the reason?

My curry function is written in JavaScript but I quickly tried to create function in Kawa to test this in implementation that probably much better then mine.

I use fold-right code from SRFI-1 code on GitHub after adding check-arg missing function.

And I used this curry version:

(define curry (lambda (f arg1) (lambda (arg2) (f arg1 arg2))))

I think that for simple testing this should be fine.

One example that this should work is JavaScript:

function type(label, arg, type) {
  // some type checking so you know which function
  // throw exception and why
  var arg_type;
  if (arg instanceof Array) {
     arg_type = 'array';
  } else if (arg === null) {
     arg_type = 'null';
  } else {
     arg_type = typeof arg;
  }
  if (arg_type !== type) {
    throw new Error(`${label}: Expecting ${type} got ${arg_type}`);
  }
}
function curry(fn, ...init_args) {
    type('curry', fn, 'function');
    var len = fn.length;
    return function() {
        var args = init_args.slice();
        function call(...more_args) {
            args = args.concat(more_args);
            //console.log({fn, len, args});
            if (args.length >= len) {
                return fn.apply(this, args);
            } else {
                return call;
            }
        }
        return call.apply(this, arguments);
    };
}


function add(...args) {
  return args.reduce((a,b) => a + b);
}

console.log(curry(curry(curry(curry(add, 1), 2), 3), 4)());

console.log([1,2,3,4].reduce((a,b) => curry(a,b), add)());

The same code works in my LIPS:

((--> #(1 2 3 4) (reduce (lambda (a b) (curry a b)) +)))
((--> #(1 2 3 4) (reduce (binary curry) +)))

NOTE: --> is macro that call reduce on array (vectors in Scheme are arrays and --> call method). lambda and binary function (inspired by Ramda library) is needed because Array::reduce add 3rd argument which is given array.

My question is why this don't work in Kawa, what I did wrong?

And I need to add that this:

(reduce curry + '(1 2 3))

also don't work in LIPS and I'm not sure why, but I'm mainly asking about standard R7RS Scheme. I wanted to test in Standard Scheme to see if my implementation is correct.

EDIT: the simple implementation of curry was in fact wrong. Here is proper implementation:

(define (curry f . args)
   (lambda more-args
      (apply f (append args more-args))))

((curry (curry (curry + 1) 2) 3) 4)
;; ==> 10

this is also simplified version the proper function, it should check length of the arguments before calling function, if it's less then original function it should keep returning lambda (not sure if you can check length of the parameters in Scheme). The above code works in Guile and Kawa.

So the other question (that remain) is how to make something like JavaScript reduce work in Scheme?

EDIT2: here is the function that works:

(define (fld fn init l)
  (do ((l l (cdr l))
       (result init (fn result (car l))))
    ((null? l) result)))


((fld curry + '(1 2 3 4)))
;; ==> 10

Tested in Kawa and Guile.

do you know any standard way to make something like fld in Scheme with default functions (without defining new functions, except curry)? For me this how reduce should work, this is how it works in JavaScript.

I've tried different combinations of fold and none of them works.


Solution

  • With help from @alinsoar I've found obsolete fold-left definition in MIT Scheme docs

    This code works in Kawa:

    (require 'list-lib) ;; import SFRI-1
    
    
    ;; this is equivalence of fold-left using fold from MIT Scheme docs
    (define (fold-left proc knil list)
       (fold (lambda (acc elt) (proc elt acc)) knil list))
    
    
    (define (curry f . args)
      "(curry f . args)
    
       simplified version of curry"
       (lambda more-args
          (apply f (append args more-args))))
    
    (display ((fold-left curry + '(1 2 3 4))))
    (newline)