I don\'t know why my code doesn\'t work.
fun lookup _ [] = 0
| lookup key ((k,v)::entries) =
if k = key
then v
else (lookup key entries)
There's nothing wrong with your code, you just didn't call lookup
with enough arguments. You make a common mistakes among beginner SML programmers coming from other languages. I'll try to clarify that.
First, the most important thing to know about functions in Standard ML is this:
All functions in Standard ML take exactly one argument.
You might be confused at this point, because your lookup
function looks as if it's taking two arguments. It kind of does, but not really.
There are two main "workarounds" (I'm using quotes because this is actually a great feature of the language) for representing functions that take multiple arguments:
If you need to write a function which, conceptually, needs three arguments, then you'd write this:
fun quux a =
fn b =>
fn c =>
(* do something with a, b and c *)
So, quux
is:
a
and returnsb
and returnsc
and returnsa
, b
and c
How would you call quux
? Like this, right?
((quux a) b) c
But function application is already left associative, so we can actually write this:
quux a b c
We don't need parentheses to "call" functions! In Standard ML parentheses don't mean "call this function". They're used just for grouping expressions together when you want to change associativity, like in mathematics: (1 + 2) * 3
.
Because defining quux
as above is really cumbersome, there's a syntactic shortcut in the language. Instead of writing this:
fun quux a =
fn b =>
fn c =>
(* do something with a, b and c *)
We can write just this:
fun quux a b c = (* do something with a, b and c *)
But, they're the same thing. quux
is still a function which takes just argument a
and returns a new function with argument b
, which returns a new function which argument c
.
Ok, so that was one way of representing multi-argument functions in Standard ML. It's also the one you used to define lookup
.
Another common way of representing multi-argument functions is to accept a tuple (which may have from 2 to as many components as you wish). Here's the above example using a tuple now:
fun quux args =
case args of
(a,b,c) => (* do something with a, b and c *)
How could we call quux
now? Like this:
quux (a,b,c)
Notice that I put a space between quux
and the tuple. It's optional, but I do it all the time to keep remembering that function application in standard ML is not represented by parentheses. quux
gets called because it's been put before the tuple (a,b,c)
. Tuples, however, do require parentheses, which is why you're seeing them above.
Again, as before, it's cumbersome to define quux
like this:
fun quux args =
case args of
(a,b,c) => (* do something with a, b and c *)
So we can actually use another great feature of the language, pattern matching in argument position, that lets us write this:
fun quux (a,b,c) = (* do something with a, b and c *)
Ok, now we can really answer your question.
You defined lookup
using the curried function syntax:
fun lookup _ [] = 0
But you "called" lookup
using the tuple syntax, where 1
is the first element of the tuple and [(1,2),(2,3)]
is the second element.
lookup (1, [(1,2),(2,3)])
Why doesn't the compiler complain, though. In this unfortunate case, it doesn't because it happens that the type of the first argument of lookup is a tuple. So, you've basically called lookup
with a single argument.
What you wanted was this:
lookup 1 [(1,2),(2,3)]
Notice that I'm not defining a tuple anymore.