iwia
iwia

Reputation: 1

Error: Operator is not a function - SML

I am in my first steps in programming and in SML, and I am trying to create a program. As a matter of fact, there are plenty of errors that occur.

In this post I get the error:

operator is not a function

I am new to functional programming, and it is difficult for me to understand the logic of the code below. Can I use multiple "val" declarations in "let", and is let/val/in used in pattern matching?

fun action(_,_,[]) = ([],[]) 
   |action(cin,cout,h::[]) = 
    let 
      val (s1,d1,d2) = (pass_cout(pass_cin h cin) cout, #1          (find2digits((pass_cout(pass_cin h cin) cout))),#2     (find2digits((pass_cout(pass_cin h cin) cout))))

  in
   if (d1<>d2) then 
    (d1::[],[])
   else ([],[])
  end
 |action (cin,cout,first::t) = 
  let
   val s1  = pass_cout ((pass_cin first cin) cout) 
   val s2 = pass_cout  (last_element t) cout (**)
   val d1 = #1 find2digits(s2)
   val d2 = #2 find2digits(s2)
   val neo_in = produce_cin s1 s2 (**)
   val neo_out = produce_cout s2

  in 
    if (neo_in<2 andalso neo_out<2) then
    (d1::action(neo_in,neo_out,cut (first::t)),d2::action(neo_in,neo_out,cut (first::t))) (**)
    else ([],[])
    end;

Upvotes: 0

Views: 949

Answers (1)

sshine
sshine

Reputation: 16135

I see you asking three questions.

Can I use multiple val declarations in let?

Yes. You're already doing this massively.

Is let/val/in[/end] used in pattern matching?

I'm not entirely sure what you mean, so I'll try to answer all questions I imagine you could be asking.

First, let-bindings allow you to temporarily bind the result of some computation or value to a name. This is useful if you need to use the result several times, and the computation is expensive, or requires side-effects that you do not wish repeated. But they can also make the code more readable (since a name might add meaning). Pattern matching allows you to handle multiple cases of different inputs in a case-by-case approach. You can match to an arbitrary depth, and the compiler will often be able to tell if you are missing any patterns.

So if you're asking if let-bindings are used in pattern matching, the answer is yes in at least the following senses:

  1. Just as a let-binding temporarily binds a computation or value to a name, the same thing occurs when you have a variable pattern in your pattern match. E.g.

    let val (a, b, c) = f (x, y) in ... end
    

    is equivalent to

    (case f (x, y) of
         (a, b, c) => ...)
    

    with a, b and c being patterns that always match and are bound to some value in ....

  2. You can have let-bindings and case-ofs inside other let-bindings or case-ofs, e.g.

    let val result =
        (case f (i, j) of
             SOME (x, y) => let val (a, b, c) = (g x, g y, g (x + y))
                            in
                              ...
                            end
           | NONE => ...)
    in ... end
    

    but these examples can often be simplified.

And if you're asking if pattern matching is used in let-bindings, the answer is yes, there is actually a bit of pattern matching going on when making a let-binding; e.g. in

let
  val (s1, d1, d2) = (pass_cout(pass_cin h cin) cout,
                      #2 (find2digits((pass_cout(pass_cin h cin) cout))),
                      #2 (find2digits((pass_cout(pass_cin h cin) cout))))
in ... end

the val (s1, d1, d2) = ... binding is using pattern matching on a 3-tuple. Pattern matching in let-bindings is only a good choice when dealing with product types and never with sum types. See What are "sums-and-products" data structures? - i.e., never do

val SOME x = f x

because it will blow up the moment f x is NONE. The case-of construction is better suited to a larger range of patterns than let-in-end, while let-in-end is better suited when making a serial range of assignments, e.g.

fun solve2 (a : real, b : real, c : real) =
    let val discr  = b * b - 4.0*a*c
        val sqr = Math.sqrt discr
        val denom = 2.0 * a
    in ((~b + sqr) / denom,
        (~b - sqr) / denom) end

Why is my program giving me the error "operator is not a function"?

As John says, it is hard to know because the code you have written will only run when supplied with meaningful definitions for the values pass_cout, pass_cin, find2digits, last_element, produce_cin and produce_cout. The way you relay the error message does not indicate what seems to be the problem, and the code you write cannot be run to reproduce the error message in full. If I had meaningful values for these, it seems there are other problems with this code where types don't unify.

Since your code does not relay its own intent (with generic names like action, s1, d2, etc.), I also cannot recommend a better way to write it, because I have no idea what it's supposed to do, so here is some general advice on coding style:

  1. Use let-bindings to avoid calling functions like find2digits twice, e.g. like

    let
      val (d1, d2) = find2digits ...
      val s1 = pass_cout ...
    in
      ...
    end
    
  2. Split your lines properly, e.g. like

    if neo_in < 2 andalso neo_out < 2
    then (d1::action(neo_in,neo_out,cut (first::t)),
          d2::action(neo_in,neo_out,cut (first::t)))
    else ([],[])
    

    and even better, like

    if neo_in < 2 andalso neo_out < 2
    then let
           val wat = action (neo_in, neo_out, cut (first::t))
         in
           (d1::wat, d2::wat)
         end
    else ([], [])
    

    where wat is replaced with some meaningful name describing what this is.

  3. Choose good variable names!

Upvotes: 2

Related Questions