From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@sympa.inria.fr Delivered-To: caml-list@sympa.inria.fr Received: from mail2-relais-roc.national.inria.fr (mail2-relais-roc.national.inria.fr [192.134.164.83]) by sympa.inria.fr (Postfix) with ESMTPS id ED1A47EE51 for ; Thu, 4 Apr 2013 20:16:26 +0200 (CEST) Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of martin.jambon@ens-lyon.org) identity=pra; client-ip=66.111.4.25; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="martin.jambon@ens-lyon.org"; x-sender="martin.jambon@ens-lyon.org"; x-conformance=sidf_compatible Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of martin.jambon@ens-lyon.org) identity=mailfrom; client-ip=66.111.4.25; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="martin.jambon@ens-lyon.org"; x-sender="martin.jambon@ens-lyon.org"; x-conformance=sidf_compatible Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of postmaster@out1-smtp.messagingengine.com) identity=helo; client-ip=66.111.4.25; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="martin.jambon@ens-lyon.org"; x-sender="postmaster@out1-smtp.messagingengine.com"; x-conformance=sidf_compatible X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: Aj8CAODCXVFCbwQZk2dsb2JhbABDxEOBBBYOAQEBAQcLCwkUBCSCHwEBBAEnEQg4AQEECwsYCRYPCQMCAQIBRQYNAQcBAYgKBq4uhEGOegaOaDMHg0CIf4osiUiONg X-IPAS-Result: Aj8CAODCXVFCbwQZk2dsb2JhbABDxEOBBBYOAQEBAQcLCwkUBCSCHwEBBAEnEQg4AQEECwsYCRYPCQMCAQIBRQYNAQcBAYgKBq4uhEGOegaOaDMHg0CIf4osiUiONg X-IronPort-AV: E=Sophos;i="4.87,410,1363129200"; d="scan'208";a="11847902" Received: from out1-smtp.messagingengine.com ([66.111.4.25]) by mail2-smtp-roc.national.inria.fr with ESMTP/TLS/ADH-AES256-SHA; 04 Apr 2013 20:16:26 +0200 Received: from compute2.internal (compute2.nyi.mail.srv.osa [10.202.2.42]) by gateway1.nyi.mail.srv.osa (Postfix) with ESMTP id CF4942065C; Thu, 4 Apr 2013 14:16:24 -0400 (EDT) Received: from frontend2.nyi.mail.srv.osa ([10.202.2.161]) by compute2.internal (MEProxy); Thu, 04 Apr 2013 14:16:24 -0400 DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= messagingengine.com; h=message-id:date:from:mime-version:to:cc :subject:references:in-reply-to:content-type :content-transfer-encoding; s=smtpout; bh=nAHJGBj15JgH30/cRmrDIU xnh/s=; b=EjGFpA0L4K1aSF9Tk2dnTjWkwOSLxK+hiY+XU6omZaMVxuCrRNKpfz TV9deHuVDhMus+H1ZlcBZlW7KWWLHj3Bw3XksqqSfIdkkp14qac2LVc4PP/GMq++ ssSEQNoiagiVf/UDbbHRtIKsx1a8duv3BkF1aYnIsDJgr+ozSYGng= X-Sasl-enc: LlBDZLBK/fOo072ukuNhtJlzhyaGkaxm4q9JM3Z1tOZq 1365099384 Received: from [192.168.1.123] (unknown [50.76.32.210]) by mail.messagingengine.com (Postfix) with ESMTPA id 4510620017F; Thu, 4 Apr 2013 14:16:24 -0400 (EDT) Message-ID: <515DC376.8030702@ens-lyon.org> Date: Thu, 04 Apr 2013 11:16:22 -0700 From: Martin Jambon User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20120827 Thunderbird/15.0 MIME-Version: 1.0 To: Alain Frisch CC: Anthony Tavener , "caml-list@inria.fr" References: <515D1B65.3070603@ens-lyon.org> <515D2CA3.2070709@frisch.fr> In-Reply-To: <515D2CA3.2070709@frisch.fr> Content-Type: text/plain; charset=windows-1252; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [Caml-list] Heterogeneous dictionary On 04/04/2013 12:32 AM, Alain Frisch wrote: > On 04/04/2013 08:19 AM, Martin Jambon wrote: >> type 'a t = ('a, (unit -> unit)) Hashtbl.t >> >> let create n = Hashtbl.create n >> >> let access () = >> let r = ref None in >> let get tbl k = >> try >> (Hashtbl.find tbl k) (); >> let result = !r in >> r := None; >> result >> with Not_found -> None >> in >> let set tbl k v = >> let v_opt = Some v in >> Hashtbl.replace tbl k (fun () -> r := v_opt) >> in >> get, set > > Careful, this is buggy! If you apply the "get" on an existing value > with the "wrong" type, it will set the slot corresponding to that wrong > type, which can result in a fake "get" later on that type. > > # let h = create 10;; > val h : '_a X.t = > # let (get1, set1) = access ();; > val get1 : '_a X.t -> '_a -> '_b option = > val set1 : '_a X.t -> '_a -> '_b -> unit = > # let (get2, set2) = access ();; > val get2 : '_a X.t -> '_a -> '_b option = > val set2 : '_a X.t -> '_a -> '_b -> unit = > # set1 h "a" true;; > - : unit = () > # set2 h "b" "X";; > - : unit = () > # get1 h "b";; > - : bool option = None > (* ====> the slot for get2/set2 has been filled! *) > > # get2 h "a";; > - : string option = Some "X" > > (* WRONG! *) Oops. Here is my corrected version: let access () = let r = ref None in let get tbl k = r := None; (* reset state in case last operation was not a get *) try (Hashtbl.find tbl k) (); let result = !r in r := None; (* clean up here in order avoid memory leak *) result with Not_found -> None in let set tbl k v = let v_opt = Some v in Hashtbl.replace tbl k (fun () -> r := v_opt) in get, set > Here is an implementation of the same interface based on local > exceptions. It avoids the bug above and it is also more efficient and > thread-safe: > > > type 'a t = ('a, exn) Hashtbl.t > > let create n = Hashtbl.create n > > let access (type t) () = > let module M = struct exception E of t end in > let r = ref None in > let get tbl k = > try > match Hashtbl.find tbl k with > | M.E x -> Some x > | _ -> None > with Not_found -> None > in > let set tbl k v = > Hashtbl.replace tbl k (M.E v) > in > get, set > > (If [get] is more frequent than [set], it is worth storing a "t option" > in the exception directly.) Nice.