0

The below code is just a kind of prototype. What I want to know is why it fails to compile.

fun test(list) = 
    let 
    fun inner(list) = 
    let
        val from = #1(hd(list))
        in
        if null(tl(list)) = false then innerinner(tl(list),from)
        else false
        end 
    fun innerinner(list,from) = 
        if #2(hd(list)) = from then true
        else if null(list) = false then innerinner(tl(list),from)
        else false
    in
    inner(list)
    end;

The error messages are:

test.txt:7.34-7.44 Error: unbound variable or constructor: innerinner
test.txt:3.2-9.6 Error: unresolved flex record
(can't tell what fields there are besides #1)
test.txt:10.2-13.13 Error: unresolved flex record
(can't tell what fields there are besides #2)

uncaught exception Error
raised at: ../compiler/Toplevel/interact/evalloop.sml:66.19-66.27
....

I am a kind beginner of ML programming. Could anyone teach me what is wrong?

Jesper.Reenberg
  • 5,944
  • 23
  • 31
Q123
  • 319
  • 5
  • 16

1 Answers1

2

You have quite a few things going on here. If we first look at the errors you are getting.

  1. unbound variable or constructor: innerinner

    In sml you can't "use" stuff before it has been declared. It is easily fixed in your case by swapping the function declarations around, and thus declaring innerinner before inner.

    If you ever end up in a case where you want to for example declare two mutually recursive functions, then this is not an option. In this case you would have to use the keyword and.

  2. unresolved flex record

    This is a bit more complicated. It is a type error and has something to do with the fact that tuples are represented as records internally (I would recommend you go read about it). Thus when you don't supply enough information, the type system will complain.

    I think this QA explains it quite good. In summary, you can't have unbounded tuples and thus you need to make it clear to the type system how many elements it contains. This could be done by explicitly type annotating the function declaration. However in general you ought to use pattern matching instead, as often as feasible.

In general you should always use pattern matching rather than tuple selectors (#1, #2, ...), or list selectors (hd or tl). You just saw why tuple selectors can be "bad" but using list selectors without testing whether or not the list is empty first will give you runtime errors (exceptions).

Putting in such test cases in your code will "blow it up" and make it messy to read. But if you use pattern matching instead you will have some nice clear cut cases in your function definition. Also often you will tend to writer less code (in my opinion).

Btw, you don't need to put parentheses around single arguments to functions, such as you main definition of the test function.

All in all your function could look something like this:

fun test list =
let
  fun innerinner ((x1, x2)::xs,from) =
      if x1 = from then true
      else innerinner(xs,from)
    | innerinner ([], from) = false

  fun inner ((x1, x2)::xs) = innerinner(xs,x1)
    | inner [] = false
in
  inner(list)
end
Community
  • 1
  • 1
Jesper.Reenberg
  • 5,944
  • 23
  • 31