0

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;
moopet
  • 6,014
  • 1
  • 29
  • 36
iwia
  • 1
  • 1
  • Your code contains multiple references to functions which are not defined (such as `find2digits` and `pass_cout`) so it is hard to say anything meaningful, especially since your intentions seem unclear. What are you trying to do? I will say this, though -- when you have multiple `val` bindings inside a `let`, they need to be delimited by semicolons rather than newlines. – John Coleman May 08 '16 at 20:53
  • 3
    @JohnColeman: Semicolons between declarations are optional, also inside let-bindings. Somewhat related, the semicolon operator between expressions (the only really useful semicolons in SML) requires a parenthesis around them, e.g. `(f x; ...; g y)` except inside let-bindings: `let ... in f x; ...; g y end`. This is in order to disambiguate it with the less useful semicolon between declarations. Since there can't be declarations directly within `let ... in` and `end` (unless an inner let-binding is introduced), the parenthesis can be omitted there. – sshine May 08 '16 at 21:36
  • @SimonShine You are right of course. I knew that but temporarily forgot it. I checked in Ullman, but he is one of those authors who tends to use semicolons a lot. – John Coleman May 09 '16 at 02:27
  • I suspect that this is because you're applying `(pass_cin first cin)` to `cout` when you define `s1`. – molbdnilo May 09 '16 at 08:50
  • thanks a lot for your help. pass_cin and pass_cout have 2 integers as input and their output is also an integer. So I have errors beginning with the one for s1, saying that operator is not a function – iwia May 09 '16 at 13:39
  • @iwia To clarify: `pass_cout ((pass_cin first cin) cout)` should most likely be `pass_cout (pass_cin first cin) cout`. – molbdnilo May 10 '16 at 14:41

1 Answers1

2

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!

Community
  • 1
  • 1
sshine
  • 15,635
  • 1
  • 41
  • 66
  • Thanks for your help. My functions pass_cin and pass_cout take 2 integers as inputs and produce 1 integer as an output. So do you have any idea why the error in s1 occurs? – iwia May 09 '16 at 19:26
  • @iwia: I am able to infer the types of these functions, so that is not my concern. You really need to focus on making your source code comprehensible as a piece of reading material. Read for example [this Best Practices guide for making code more readable](http://code.tutsplus.com/tutorials/top-15-best-practices-for-writing-super-readable-code--net-8118). (The examples are not in ML, but they should be sufficiently general to learn from anyway.) – sshine May 10 '16 at 08:32