Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

This Value is Not a Function and Cannot be Applied F#

Tags:

f#

I have been trying to write a program in F# which takes two values (a tuple): a number and a string. Depending on whether the string tells the program to add or multiply, it will add or multiply the input number with all the integers from 1 to that number (i.e. 1..n).

Here is the code

let addormult (n:int, what:string) = 
    if what = "multiply" then 
       let rec product n = 
           if n<1 then 0
           else n*product(n-1)
       printfn "%A" product
    elif what = "sum" then
         let rec sum = 
             if n<1 then 0
             else n + sum(n-1)
         printfn "%A" sum

However, every time I try to run this function, I get the error

"This value is not a function and cannot be applied."

So what am I doing wrong?

like image 765
Clement Decker Avatar asked Aug 10 '16 19:08

Clement Decker


2 Answers

Really more of an extended comment, but the whole of this code can be simplified by using a fold function.

let addormult (n, what) =
  let f = if what = "multiply" then (*) else (+)
  List.fold f 1 [1..n]
let x = addormult(4, "multiply") // results in x = 24
let y = addormult(4, "add")      // results in y = 10

Or even better, define getOp outside the scope, since it's going to be generally applicable.

let getOp what = if what = "multiply" then (*) else (+)
let addormult (n, what) = List.fold (getOp what) 1 [1..n]
let x = addormult(4, "multiply") // results in x = 24
let y = addormult(4, "add")      // results in y = 10

fold is also tail-recursive, ensuring you don't exceed the stack size limit for large N. In F#, a lot of times when you do recursion, there's already a standard library function that does what you need. Or if not, sometimes it's better to extract a "general" recursion out of your function and then implement your specific thing in terms of that.

Now, note that strings are not the best way to convey intention here. Far better to use a discriminated union to convey your operation:

type Op = Add | Multiply
let getop = function | Multiply -> (*) | Add -> (+)
let addormult (n, what) = List.fold (getop what) 1 [1..n]
let x = addormult(4, Multiply) // results in x = 24
let y = addormult(4, Add)      // results in y = 10

That way there's no chance someone could accidentally type "mutliply" and get an unexpected return value.

But really there's no reason to limit to just two operations. It's straightforward to make this work with any operation:

let doOneToN f n = List.fold f 1 [1..n]
let x0 = doOneToN (*) 4 // results in x0 = 24
let y0 = doOneToN (+) 4 // results in y0 = 10

With this, you can easily use partial application to make specialized functions if you want:

let factorial = doOneToN (*)     // the `n` argument is "curried"
let triangularSum = doOneToN (+) // the `n` argument is "curried"
let x1 = factorial 4             // results in x1 = 24
let y1 = triangularSum 4         // results in y1 = 10

That's the nice thing about functional programming, is that it's easy to mix and match things.

Of course it's so simple that you might not even care to make a function. It's almost simpler just to call let x = List.fold (*) 1 [1..4] inline. Functional-first languages tend to be nice and terse that way.

like image 80
Dax Fohl Avatar answered Sep 19 '22 18:09

Dax Fohl


Prefix: I am no F# expert and someone more knowledgeable than me may be able to provide a more complete answer.

There are a couple of things to fix here:

  1. let rec sum = is missing a parameter variable declaration.
  2. printfn "%A" product (and the one for sum) are printing the functions, not the values. To correct this, you need to call the function with the arguments.
  3. Defining n for both the addormult tuple value and the recursive value might cause issues - though I am not sure how F# deals with scoping in a situation like this.

This is the "multiply" version after these changes:

let addormult (n:int, what:string) = 
if what = "multiply" then 
   let rec product x = 
       if x = 1 then 1
       else x * product(x-1)       
   printfn "product is %A" (product n)

And here is the call:

let x = addormult (4, "multiply")

Which gives me this value:

product is 24
like image 41
Max Avatar answered Sep 20 '22 18:09

Max