In the function
fun exists dict key =
case dict of
[] => []
| (k, _ )::rep => if k = key
then true
else exists rep key
I spot two issues:
- You can't return
[]
in one place and true
in another.
- Instead of
if P then true else Q
, write P orelse Q
.
You're using :>
which means that the module is opaque, so you can only access the things specified in the signature. The internal list representation is not mentioned in the signature, so you can't refer to a dict as a list, even though you may know that that's how it's implemented. This is a feature.
I would probably call exists
for member
, since List.exists
is a higher-order predicate, e.g. List.exists (fn x => x > 5) [3, 6, 9]
. You could also deviate from any standard library naming and say containsKey
and containsValue
, or something like that.
Besides the insert
function that molbdnilo suggested, you may like to have a fromList
function.
Here's a refactored version (comments omitted for brevity, but I think your comments are good!):
signature DICTIONARY =
sig
type (''key, 'value) dict
val empty: (''key, 'value) dict
val member: ''key -> (''key, 'value) dict -> bool
val insert: (''key * 'value) -> (''key, 'value) dict -> (''key, 'value) dict
val fromList: (''key * 'value) list -> (''key, 'value) dict
end
structure Dictionary :> DICTIONARY =
struct
type (''key, 'value) dict = (''key * 'value) list
val empty = []
fun member key [] = false
| member key ((key2, _)::dict) =
key = key2 orelse member key dict
fun insert (key, value) [] = [(key, value)]
| insert (key, value) ((key2, value2)::dict) =
if key = key2
then (key, value) :: dict
else (key2, value2) :: insert (key, value) dict
fun fromList pairs = foldl (fn (pair, dict) => insert pair dict) empty pairs
end
But since you're building a dictionary module, there are two things you want to consider:
- Make it possible to use some kind of binary tree as internal representation, requiring that the keys can be ordered rather than compared for equality.
- Since Standard ML doesn't have special syntax like
''key
to express that something can be ordered (Haskell generalises this as type classes, but Standard ML has only the special syntax ''key
), this is a good case for using functors, which is the name given to higher-order modules, aka parameterised modules.
Here's an example signature, functor and structure that you can fill out:
signature ORD = sig
type t
val compare : t * t -> order
end
signature DICT = sig
type key
type 'value dict
val empty: 'value dict
val member: key -> 'value dict -> bool
val insert: key * 'value -> 'value dict -> 'value dict
val fromList: (key * 'value) list -> 'value dict
end
functor Dict (Ord : ORD) :> DICT = struct
type key = Ord.t
type 'value dict = (key * 'value) list
val empty = ...
fun member _ _ = raise Fail "not implemented"
fun insert _ _ = raise Fail "not implemented"
fun fromList _ = raise Fail "not implemented"
end
At this point you can change type 'value dict
into using a binary tree, and when you need to decide whether to go left or right in this binary tree, you can write:
case Ord.compare (key1, key2) of
LESS => ...
| EQUAL => ...
| GREATER => ...
And when you need a dictionary where the key is some particular orderable type, you can create a module using this functor:
structure IntDict = Dict(struct
type t = int
val compare = Int.compare
end)
structure StringDict = Dict(struct
type t = string
val compare = String.compare
end)
See also Standard ML functor examples for more examples.