caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: David House <dhouse@janestreet.com>
To: Yaron Minsky <yminsky@janestreet.com>
Cc: Mark Shinwell <mshinwell@janestreet.com>,
	Ollie Frolovs <of12343@my.bristol.ac.uk>,
	 caml users <caml-list@inria.fr>
Subject: Re: [Caml-list] Creating a Map from custom type
Date: Thu, 14 Nov 2013 12:11:37 +0000	[thread overview]
Message-ID: <CAK=fH+j6L3BD5nswGrH6_8Yw9J3WSY4mJTnsYXbV-GJPuP_dbA@mail.gmail.com> (raw)
In-Reply-To: <CACLX4jT7oUTcF8X9edu3J434BFj+4-qvzwbYC5TK9LJNfkQUeQ@mail.gmail.com>

To follow up on Mark's example, you can use the following slightly
more general idiom:

module My_thing = struct
  module T = struct
    type t = int * string * string with sexp, compare
  end
  include T
  include Comparable.Make(T)

  let do_some_stuff t = ...
end

Then My_thing will satisfy the Comparable signature, which means
you'll have My_thing.Map, as well as My_thing.Set, My_thing.equal,
My_thing.(<) etc.

We do this on basically every type in core, since you'll want to use
everything in a set sooner or later :)

The "gold standard" for such types is the Identifiable signature. This
is basically the union of the Comparable signature, the Hashable
signature (which gives you hashtables, hash-sets etc.) and the
Stringable signature (to/from strings). You can get there with just a
little more work:

module My_thing = struct
  module T = struct
    type t = ... with sexp, compare, bin_io
    let hash = Hashtbl.hash
    let to_string t = ...
    let of_string t = ...
    let module_name = "My_lib.My_thing" (* for the toplevel *)
  end
  include T
  include Identifiable.Make(T)

  (* other functions here *)
end

If you want the string representation to be the same as the sexp
representation, that's possible too with only a little more
clunkiness:

module My_thing = struct
  module T = struct
    module T' = struct
      type t = ... with sexp, compare, bin_io
    end
    include T'
    include Sexpable.To_stringable(T')
    let hash = Hashtbl.hash
    let module_name = "My_lib.My_thing" (* for the toplevel *)
  end
  include T
  include Identifiable.Make(T)

 (* other functions here *)
end

On 14 November 2013 12:03, Yaron Minsky <yminsky@janestreet.com> wrote:
> It's worth noting that if you want a simply polymorphic map, you can
> have one, you just need to be explicit about what you're doing:
>
> utop # let map = Map.Poly.of_alist_exn [1, "one"; 2, "two"; 3, "three"];;
> val map : (int, string) Map.Poly.t = <abstr>
>
> which is probably the lowest-pain approach.  Polymorphic compare
> (which is what this is based on) has some downside, but is perfectly
> good for many use-cases.  RWO covers in detail the pitfalls of
> polymorphic compare, but it doesn't mean you should avoid it,
> especially when you're getting started.
>
> As for David's point about Core vs the stdlib, I think creating a map
> with a polymorphic comparison function is actually quite a bit easier
> in Core than it is with the stdlib, since no functor application is
> required.
>
> y
>
> On Thu, Nov 14, 2013 at 6:21 AM, Mark Shinwell <mshinwell@janestreet.com> wrote:
>> On 14 November 2013 10:39, Ollie Frolovs <of12343@my.bristol.ac.uk> wrote:
>>> My current implementation uses associative lists, in subroute.ml:
>>>
>>> open Core.Std
>>> type t = ((int list * int) * (int * int list)) list
>>> let empty = []
>>> let to_list x = x
>>> let add t k v = List.Assoc.add t k v
>>> let find t k = List.Assoc.find t k
>>>
>>> I would like to use a Map instead. The key for my Map must have the type (int list * int) and the values have the type (int * int list). If i understood it correctly, because the key is a tuple and not one of the built-in simple data types (string, int, etc), because of this i have to provide a custom comparator. This is first thing that i am not sure of.
>>
>> I think you need something like:
>>
>> module Key = struct
>>   type t = int list * int with sexp, compare
>> end
>>
>> module My_map = Map.Make (Key)
>>
>> Then, for example:
>>
>>   let map =
>>     My_map.add My_map.empty
>>       ~key:([42], 0)
>>       ~data:(1, [2])
>>
>> The "with compare" auto-generates a "compare" function for
>> you.  Using explicit comparison functions, as Core strongly
>> encourages, does unfortunately mean a bit more code at times
>> but is arguably vastly more sustainable as your codebase
>> grows.  In such scenarios it becomes increasingly unlikely
>> that the correct notion of comparison on one of your many
>> abstract types coincides with the structural comparison
>> on whatever implementation those types happen to have today.
>>
>> Mark
>>
>> --
>> Caml-list mailing list.  Subscription management and archives:
>> https://sympa.inria.fr/sympa/arc/caml-list
>> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
>> Bug reports: http://caml.inria.fr/bin/caml-bugs
>
> --
> Caml-list mailing list.  Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs

  reply	other threads:[~2013-11-14 12:11 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-11-14 10:39 Ollie Frolovs
2013-11-14 10:56 ` David Allsopp
2013-11-14 11:02 ` Malcolm Matalka
2013-11-14 11:21 ` Mark Shinwell
2013-11-14 12:03   ` Yaron Minsky
2013-11-14 12:11     ` David House [this message]
2013-11-15  9:36       ` Ollie Frolovs

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAK=fH+j6L3BD5nswGrH6_8Yw9J3WSY4mJTnsYXbV-GJPuP_dbA@mail.gmail.com' \
    --to=dhouse@janestreet.com \
    --cc=caml-list@inria.fr \
    --cc=mshinwell@janestreet.com \
    --cc=of12343@my.bristol.ac.uk \
    --cc=yminsky@janestreet.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).