0

i'm playing a bit with higher order functions in kotlin.

I would like to have a list of functions, and then reduce the list to one single result by calling all the functions in the list and summing them. Something like this:

val num =  arrayOf(1, 2, 3, 4)

val funcs = num.map <Int, (Int) -> Int > { x  -> {
        y -> y + x
}}

val res = funcs.reduce{acc, elem -> elem(1) + acc}

But the bottom line here does not compile, and claims that my lambda in the reduction returns Unit, which doesnt make sense to me. I also tried calling reduce like this, same issue persists:

val res = funcs.reduce{acc, elem -> elem.invoke(1) + acc}

Why does the invocation of a Int -> Int lambda return Unit? and how do I make this work

Edit:

Strangely enough, this works:

val res = funcs.fold(0){acc, func -> func(0) + acc}
GarseB010
  • 27
  • 5

3 Answers3

1

I suspect you're confusing the reduce() function with the fold() function.

Both are higher-order functions which apply an operation consecutively to values in a list (or array) to combine them into a single result.

reduce() is the simpler function, in which the final result is the same type as the list elements.  This means that the operation must take two values of the same type, and return a value of that same type.*  For example, you could add up list of integers by calling reduce() on it and giving addition function — one which adds two integers to give another integer, so the partial sums and the final sum are integers too.

fold() is a more general function, in which the final result can be an unrelated type.  This makes it more powerful, but also a little more complex.  The operation you give must take one parameter of the list type, but another (the accumulator) of the result type, and it must return the result type.  And because it can't ‘bootstrap’ in the same way as reduce(), you also need to give an initial value for the accumulator (e.g. 0 for addition).

In your case, I'm not entirely sure what you're trying to achieve (other than learning about higher-order functions, which is of course a perfectly valid goal!), so I don't have a `solution’ as such.

Your list contains functions, so a reduce() would have to return a function as well.  This is possible, but combining functions to give another function is complex, e.g.:

val res = funcs.reduce{ acc, elem -> { i -> elem(i) + acc(i) } }

However, a fold() would also work [as, I see from your edit, you've discovered] and would seem closer to your aim:

val res = funcs.fold(0){ acc, elem -> elem(1) + acc }

(* Actually, the result type of reduce() can be a supertype of the element type, as you can see from the signature: inline fun <S, T : S> Iterable<T>.reduce(operation: (acc: S, T) -> S): S.  But it can't be an unrelated type.)

gidds
  • 16,558
  • 2
  • 19
  • 26
0

The reduce function takes a lambda (function) which this lambda function takes two parameters: acc and it and returns a value with the same type as acc

public inline fun <S, T : S> Iterable<T>.reduce(operation: (acc: S, T) -> S): S

in your case both acc and elem are functions that return an integer but in your code you are not returning anything

here is the edited code

val res1 = funcs.reduce { acc, elem ->
    { 
        elem(1) + acc(1) 
    }
}

In Kotlin lambda functions returning values are by only mentioning them at the end of lambdas and you don't need to use the return word

0

I am not well versed in Kotlin so forgive my style but something in the question/answers got my attention, for it could be symptomatic of a misunderstanding.

The most meaningful way to use fold here was the following

val res = funcs.fold(0){x, f -> f(x)}

Your way of forcing addition by applying 0 or 1 to the current value was a bit clumsy considering funcs is already a list of partially applied + operations waiting to be applied with a second argument.


Concerning reduce, it would work similarly with the only difference that the result would be a function which adds 10 to its input.

val inc = funcs.reduce{f, g -> {x -> g(f(x))}}
val res = inc(2) // 12

The problem being that reduce throws when the input array is empty. fold solves that for us:

val id = {x: Int -> x}
val inc = funcs.fold(id){f, g -> {x -> g(f(x))}}

Now if the array is empty, inc will simply spit out its argument untouched.

Note that I shouldn't have to cast x: Int. That's where my Kotlin foo falls apart: lambdas can't have generic types and regular functions can't be passed around (or so it seems).

If you don't find a solution to this problem, you will have a hard time doing functional programming in Kotlin.


If you want to learn more about reduce/fold, there is an in-depth answer which deserves more love here. The language used is Javascript but the discussion is more general.

geoffrey
  • 2,080
  • 9
  • 13