Murphy
Murphy

Reputation: 47

Unbound constructor error when using module signature

module type Cont_map = sig
  type t
  type key
  type value = key (* values are the same type as keys *)
  val empty : t

  val lookup : key -> t -> value option

  val insert : key -> value -> t -> t

  val remove : key -> t -> t * value option

  val lub_key : key -> t -> key option

  val glb_key : key -> t -> key option

  val interpolated_lookup : key -> t -> value option

end

module Cont_map (Key : Interp with type t = float) : Cont_map = struct 
  type t = 
    | Leaf 
    | Branch of Key.t * Key.t * t * t

  type key = Key.t
  type value = key

  let empty = Leaf

  let rec lookup k dmap = 
    match dmap with
    | Leaf -> None
    | Branch (kk, vv, l, r) -> if k < kk then lookup k l else if k > kk then lookup k r else Some(vv)

  let rec insert kk vv dmap = 
    match dmap with
    | Leaf -> Branch (kk, vv, Leaf, Leaf)
    | Branch (k, v, l, r) -> 
      if kk < k then Branch (k, v, insert kk vv l, r)
      else if kk = k then Branch (k, vv, l, r)
      else Branch (k, v, l, insert kk vv r)

  let remove kk dmap = 
    let rec max_key (d: t) = 
      match d with
      | Leaf -> None
      | Branch (key, value, _, right) -> 
        match right with
        | Leaf -> Some(key, value)
        | Branch (_, _, _, _) -> max_key(right)
    in
    let rec remove_internal kk map =
      match map with
      | Leaf -> remove_internal kk map
      | Branch (k, v, l, r) -> 
        if kk < k then Branch (k, v, remove_internal kk l, r)
        else if kk = k then 
          match max_key(l) with
          | Some(key, value) -> Branch (key, value, remove_internal key l, r)
          | None -> r
        else Branch (k, v, l, remove_internal kk r)
    in
    match lookup kk dmap with
    | None -> (dmap, None)
    | Some(v) -> (remove_internal kk dmap, Some(v))
    
  let rec lub_key kk map = 
    let rec max_key (d: t) = 
      match d with
      | Leaf -> None
      | Branch (key, _, _, right) -> 
        match right with
        | Leaf -> Some(key)
        | Branch (_, _, _, _) -> max_key(right)
    in
    match map with
    | Leaf -> None
    | Branch (k, _, l, r) -> if kk < k then
      match max_key(l) with
      | None -> Some(k)
      | Some(kkk) -> if kkk < kk then Some(k) else lub_key kk l 
  else if kk = k then Some(k) else lub_key kk r

  let rec glb_key kk map =
    let rec min_key (d: t) = 
      match d with
      | Leaf -> None
      | Branch (key, _, left, _) -> 
        match left with
        | Leaf -> Some(key)
        | Branch (_, _, _, _) -> min_key(left)
    in
    match map with
    | Leaf -> None
    | Branch (k, _, l, r) -> if kk < k then glb_key kk l else if kk = k then Some(k)
    else match min_key(r) with
    | None -> Some(k)
    | Some(kkk) -> if kkk <= kk then glb_key kk r else Some(k)

  let interpolated_lookup kk map = 
    match lub_key kk map with
    | None -> None
    | Some(rk) -> match glb_key kk map with
                  | None -> None
                  | Some(lk) -> match lookup lk map, lookup rk map with
                                | Some(lv), Some(rv) -> Some(Key.interpolate (lk, lv) (rk, rv) kk)
                                | _ -> failwith "Can't reach here"

end

module Float_cont_map = Cont_map (Float_interp)

Above is my code in a file named abstraction.ml

open Core;;
open OUnit2;;

module M = Abstraction.Cont_map(Abstraction.Float_interp);;

let empty = M.empty

let d1 = M.(Branch (1.0, 1.0, Leaf, Branch (2.0, 2.0, Leaf, Branch (3.0, 3.0, Leaf, Branch (4.0, 4.0, Leaf, Leaf)))))

This is my test file named tests.ml

(test
 (name tests)
 (libraries 
  core
  ounit2
  abstraction
 ))

This is my dune file in tests directory.

However, it calls that "Unbound constructor Branch". And in code below I write:

let test_lookup _ = 
  assert_equal None @@ (M.lookup 1. empty);
  assert_equal (Some(1.0)) @@ (M.lookup 1. d1)

It doesn't say that M.lookup unbound value, but it says that expected M.value type but get float.

Upvotes: 0

Views: 210

Answers (1)

ivg
ivg

Reputation: 35210

Like functions, e.g., empty, the constructors, e.g., Leaf are also a part of the signature. So when you constrained your module type with the signature that lacks constructors, you effectively hid it from the users of your module.

You should either make them public, e.g.,

module type Cont_map = sig
  type key
  type t = 
   | Leaf 
   | Branch of key * key * t * t

But this will break the abstraction of your module, since you don't want the users to create containers directly. So the right approach would be to use the public interface of your module to create containers, i.e., to use empty and insert.

You can also use inline_tests and test the implementation of the container module inside of your module, where you have access to your constructors.

Upvotes: 1

Related Questions