* [Caml-list] Heterogeneous dictionary @ 2013-04-04 0:45 Anthony Tavener 2013-04-04 1:29 ` Yaron Minsky ` (2 more replies) 0 siblings, 3 replies; 17+ messages in thread From: Anthony Tavener @ 2013-04-04 0:45 UTC (permalink / raw) To: caml-list [-- Attachment #1: Type: text/plain, Size: 2942 bytes --] I think I might be up against a brick wall. But maybe there's a door I don't see, without Obj.magicing myself through the wall. (I haven't had to use magic for anything yet!) :) I want to stash values under a key, and retrieve them by that key. All values bound to a given key are of the same type, but the type will differ between keys. Basically like a hashtable you'd find in a dynamic language. (* modifier-additions like this would be scattered across the code-base *) contribute `RecoveryRoll (fun (a,b) -> a+3,b) contribute `RecoveryRoll (fun (a,b) -> a,b-1) contribute `ResistPain (fun a -> a+1) contribute `MagicResistance (fun a -> match a with None -> None | Some x -> Some(x+3)) contribute `MagicResistance (fun a -> match a with None -> Some 7 | Some x -> Some(x+7)) (* example of applying modifiers... *) let modified = fold `RecoveryRoll (4,1) in ... (There are details I've left out here, like the need for 'contribute' returning an id by which to remove a modifier, as well as control over order-of-application.) Now I think the type signature of these functions would be: val contribute: a'. 'a key -> ('a -> 'a) -> unit val fold: 'a. 'a key -> 'a -> 'a And the thorn in my side would be that the key must be "keyed" to the type, or is there an escape? At one point I tried a universal type to "hide" the signature of the function-list, but I also stashed the inj/proj under the key -- well, of course the inj/proj functions had different types per entry in a hashtable, so that worked as well as not having a universal type involved. :) If I provide the inj/proj functions at each invocation then I need to pre-create these and house them somewhere (which could create a bottleneck of type-dependencies) -- Imagine several hundred modifiers like `RecoveryRoll; some might use types that only need visibility in one module. Trying to place each modifier in suitable modules also seems a mess... a lot of them conceptually exist "in the spaces between modules". So I keep trying to create an airy light-weight "implied" association to connect modifiers to use-sites... but to satisfy typing it seems I need to be explicit at some point. Does anyone have any ideas? I'm often surprised at the gymnastics OCaml's type-system can accomplish under the guidance of some smart folks. If anyone's made a heterogenous dictionary/hashtable that doesn't need types explicity declared, that would probably be what I'm looking for. Note that I've had this problem surface several times and managed to find solutions that suited the specific problem, but each problem can have it's subtle details. In this case, the large number of keys and functions, combined with their spread across codebase and the sparse nature of their use (a game-entity might have a few dozen modifiers out of hundreds)... really seems to push for association-by-name-only. At least that's all my brain gravitates toward. -Tony [-- Attachment #2: Type: text/html, Size: 3840 bytes --] ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-04 0:45 [Caml-list] Heterogeneous dictionary Anthony Tavener @ 2013-04-04 1:29 ` Yaron Minsky 2013-04-04 2:18 ` Anthony Tavener 2013-04-04 6:19 ` Martin Jambon 2013-04-04 7:38 ` Raphaël Proust 2 siblings, 1 reply; 17+ messages in thread From: Yaron Minsky @ 2013-04-04 1:29 UTC (permalink / raw) To: Anthony Tavener; +Cc: caml-list Have you looked at Univ_map in Core? It has the same problem you're complaining about, in that the keys do need to be tied to the type of the data stored under that key. You then of course need to stash the key somewhere. But I'm not sure what problem that creates. In particular, I don't know why this creates a bottleneck of type dependencies. Each Univ_map.Key.t is created independently, and can be created at any point in the code. y On Wed, Apr 3, 2013 at 8:45 PM, Anthony Tavener <anthony.tavener@gmail.com> wrote: > I think I might be up against a brick wall. But maybe there's a door I don't > see, without Obj.magicing myself through the wall. (I haven't had to use > magic > for anything yet!) :) > > I want to stash values under a key, and retrieve them by that key. All > values > bound to a given key are of the same type, but the type will differ between > keys. Basically like a hashtable you'd find in a dynamic language. > > (* modifier-additions like this would be scattered across the code-base *) > contribute `RecoveryRoll (fun (a,b) -> a+3,b) > contribute `RecoveryRoll (fun (a,b) -> a,b-1) > contribute `ResistPain (fun a -> a+1) > contribute `MagicResistance (fun a -> match a with None -> None | Some x > -> Some(x+3)) > contribute `MagicResistance (fun a -> match a with None -> Some 7 | Some x > -> Some(x+7)) > > (* example of applying modifiers... *) > let modified = fold `RecoveryRoll (4,1) in > ... > > (There are details I've left out here, like the need for > 'contribute' returning an id by which to remove a modifier, as well > as control over order-of-application.) > > Now I think the type signature of these functions would be: > > val contribute: a'. 'a key -> ('a -> 'a) -> unit > val fold: 'a. 'a key -> 'a -> 'a > > And the thorn in my side would be that the key must be "keyed" to > the type, or is there an escape? At one point I tried a universal > type to "hide" the signature of the function-list, but I also > stashed the inj/proj under the key -- well, of course the inj/proj > functions had different types per entry in a hashtable, so that > worked as well as not having a universal type involved. :) > > If I provide the inj/proj functions at each invocation then I need > to pre-create these and house them somewhere (which could create a > bottleneck of type-dependencies) -- Imagine several hundred > modifiers like `RecoveryRoll; some might use types that only need > visibility in one module. Trying to place each modifier in suitable > modules also seems a mess... a lot of them conceptually exist "in > the spaces between modules". > > So I keep trying to create an airy light-weight "implied" > association to connect modifiers to use-sites... but to satisfy > typing it seems I need to be explicit at some point. > > Does anyone have any ideas? I'm often surprised at the gymnastics > OCaml's type-system can accomplish under the guidance of some smart > folks. If anyone's made a heterogenous dictionary/hashtable that > doesn't need types explicity declared, that would probably be what > I'm looking for. > > > Note that I've had this problem surface several times and managed to > find solutions that suited the specific problem, but each problem > can have it's subtle details. In this case, the large number of keys > and functions, combined with their spread across codebase and the > sparse nature of their use (a game-entity might have a few dozen > modifiers out of hundreds)... really seems to push for > association-by-name-only. At least that's all my brain gravitates > toward. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-04 1:29 ` Yaron Minsky @ 2013-04-04 2:18 ` Anthony Tavener 0 siblings, 0 replies; 17+ messages in thread From: Anthony Tavener @ 2013-04-04 2:18 UTC (permalink / raw) To: Yaron Minsky; +Cc: caml-list [-- Attachment #1: Type: text/plain, Size: 5000 bytes --] Oh, maybe I created a limitation in my own head... Would this work then (for example): module Modifier = struct let recovery = Key.create "recovery" let resist_pain = Key.create "resist_pain" let resist_magic = Key.create "resist_magic" (* a whole lot of these... *) end These would then be monomorphic? Anyway, the important thing is they didn't require knowledge of any types to create all the keys, so now having all other modules referencing Modifier wouldn't be bad at all. Then something like the following will reify the type of the Modifier.recovery key? let mods = set mods Modifier.recovery (fun (a,b) -> a+6,b) in ... I kept thinking that if I had a "grand central" of universal embeddors they'd need to be aware of the types they're embedding... thereby creating the type-dependencies I was worried about! This solution, if I'm understanding it right, is perfectly fine! Thank-you for cluing me in, Yaron! On Wed, Apr 3, 2013 at 7:29 PM, Yaron Minsky <yminsky@janestreet.com> wrote: > Have you looked at Univ_map in Core? It has the same problem you're > complaining about, in that the keys do need to be tied to the type of > the data stored under that key. You then of course need to stash the > key somewhere. But I'm not sure what problem that creates. In > particular, I don't know why this creates a bottleneck of type > dependencies. Each Univ_map.Key.t is created independently, and can > be created at any point in the code. > > y > > On Wed, Apr 3, 2013 at 8:45 PM, Anthony Tavener > <anthony.tavener@gmail.com> wrote: > > I think I might be up against a brick wall. But maybe there's a door I > don't > > see, without Obj.magicing myself through the wall. (I haven't had to use > > magic > > for anything yet!) :) > > > > I want to stash values under a key, and retrieve them by that key. All > > values > > bound to a given key are of the same type, but the type will differ > between > > keys. Basically like a hashtable you'd find in a dynamic language. > > > > (* modifier-additions like this would be scattered across the > code-base *) > > contribute `RecoveryRoll (fun (a,b) -> a+3,b) > > contribute `RecoveryRoll (fun (a,b) -> a,b-1) > > contribute `ResistPain (fun a -> a+1) > > contribute `MagicResistance (fun a -> match a with None -> None | Some > x > > -> Some(x+3)) > > contribute `MagicResistance (fun a -> match a with None -> Some 7 | > Some x > > -> Some(x+7)) > > > > (* example of applying modifiers... *) > > let modified = fold `RecoveryRoll (4,1) in > > ... > > > > (There are details I've left out here, like the need for > > 'contribute' returning an id by which to remove a modifier, as well > > as control over order-of-application.) > > > > Now I think the type signature of these functions would be: > > > > val contribute: a'. 'a key -> ('a -> 'a) -> unit > > val fold: 'a. 'a key -> 'a -> 'a > > > > And the thorn in my side would be that the key must be "keyed" to > > the type, or is there an escape? At one point I tried a universal > > type to "hide" the signature of the function-list, but I also > > stashed the inj/proj under the key -- well, of course the inj/proj > > functions had different types per entry in a hashtable, so that > > worked as well as not having a universal type involved. :) > > > > If I provide the inj/proj functions at each invocation then I need > > to pre-create these and house them somewhere (which could create a > > bottleneck of type-dependencies) -- Imagine several hundred > > modifiers like `RecoveryRoll; some might use types that only need > > visibility in one module. Trying to place each modifier in suitable > > modules also seems a mess... a lot of them conceptually exist "in > > the spaces between modules". > > > > So I keep trying to create an airy light-weight "implied" > > association to connect modifiers to use-sites... but to satisfy > > typing it seems I need to be explicit at some point. > > > > Does anyone have any ideas? I'm often surprised at the gymnastics > > OCaml's type-system can accomplish under the guidance of some smart > > folks. If anyone's made a heterogenous dictionary/hashtable that > > doesn't need types explicity declared, that would probably be what > > I'm looking for. > > > > > > Note that I've had this problem surface several times and managed to > > find solutions that suited the specific problem, but each problem > > can have it's subtle details. In this case, the large number of keys > > and functions, combined with their spread across codebase and the > > sparse nature of their use (a game-entity might have a few dozen > > modifiers out of hundreds)... really seems to push for > > association-by-name-only. At least that's all my brain gravitates > > toward. > > -- > 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 > [-- Attachment #2: Type: text/html, Size: 6822 bytes --] ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-04 0:45 [Caml-list] Heterogeneous dictionary Anthony Tavener 2013-04-04 1:29 ` Yaron Minsky @ 2013-04-04 6:19 ` Martin Jambon 2013-04-04 7:32 ` Alain Frisch 2013-04-04 7:38 ` Raphaël Proust 2 siblings, 1 reply; 17+ messages in thread From: Martin Jambon @ 2013-04-04 6:19 UTC (permalink / raw) To: Anthony Tavener; +Cc: caml-list Here is an old trick, which is not necessarily useful but fun nonetheless: (* mixtbl.mli *) type 'a t (** A hash table containing values of different types. The type parameter ['a] represents the type of the keys. *) val create : int -> 'a t (** [create n] creates a hash table of initial size [n]. *) val access : unit -> ('a t -> 'a -> 'b option) * ('a t -> 'a -> 'b -> unit) (** Return a pair (get, set) that works for a given type of values. This function is normally called once for each type of value. Several getter/setter pairs may be created for the same type, but a value set with a given setter can only be retrieved with the matching getter. The same getters and setters may be reused across multiple tables. *) (* mixtbl.ml *) 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 I put it all on Github: https://github.com/mjambon/mixtbl On 04/03/2013 05:45 PM, Anthony Tavener wrote: > I think I might be up against a brick wall. But maybe there's a door I don't > see, without Obj.magicing myself through the wall. (I haven't had to use > magic > for anything yet!) :) > > I want to stash values under a key, and retrieve them by that key. All > values > bound to a given key are of the same type, but the type will differ between > keys. Basically like a hashtable you'd find in a dynamic language. > > (* modifier-additions like this would be scattered across the > code-base *) > contribute `RecoveryRoll (fun (a,b) -> a+3,b) > contribute `RecoveryRoll (fun (a,b) -> a,b-1) > contribute `ResistPain (fun a -> a+1) > contribute `MagicResistance (fun a -> match a with None -> None | > Some x -> Some(x+3)) > contribute `MagicResistance (fun a -> match a with None -> Some 7 | > Some x -> Some(x+7)) > > (* example of applying modifiers... *) > let modified = fold `RecoveryRoll (4,1) in > ... > > (There are details I've left out here, like the need for 'contribute' > returning an id > by which to remove a modifier, as well as control over > order-of-application.) > > Now I think the type signature of these functions would be: > > val contribute: a'. 'a key -> ('a -> 'a) -> unit > val fold: 'a. 'a key -> 'a -> 'a > > And the thorn in my side would be that the key must be "keyed" to the > type, or > is there an escape? At one point I tried a universal type to "hide" the > signature of the function-list, but I also stashed the inj/proj under > the key > -- well, of course the inj/proj functions had different types per entry in a > hashtable, so that worked as well as not having a universal type > involved. :) > > If I provide the inj/proj functions at each invocation then I need to > pre-create these and house them somewhere (which could create a > bottleneck of > type-dependencies) -- Imagine several hundred modifiers like `RecoveryRoll; > some might use types that only need visibility in one module. Trying to > place > each modifier in suitable modules also seems a mess... a lot of them > conceptually exist "in the spaces between modules". > > So I keep trying to create an airy light-weight "implied" association to > connect modifiers to use-sites... but to satisfy typing it seems I need > to be > explicit at some point. > > Does anyone have any ideas? I'm often surprised at the gymnastics OCaml's > type-system can accomplish under the guidance of some smart folks. If > anyone's > made a heterogenous dictionary/hashtable that doesn't need types explicity > declared, that would probably be what I'm looking for. > > > Note that I've had this problem surface several times and managed to find > solutions that suited the specific problem, but each problem can have it's > subtle details. In this case, the large number of keys and functions, > combined with their spread across codebase and the sparse nature of their > use (a game-entity might have a few dozen modifiers out of hundreds)... > really > seems to push for association-by-name-only. At least that's all my brain > gravitates toward. > > -Tony > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-04 6:19 ` Martin Jambon @ 2013-04-04 7:32 ` Alain Frisch 2013-04-04 18:16 ` Martin Jambon 0 siblings, 1 reply; 17+ messages in thread From: Alain Frisch @ 2013-04-04 7:32 UTC (permalink / raw) To: Martin Jambon, Anthony Tavener; +Cc: caml-list 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 = <abstr> # let (get1, set1) = access ();; val get1 : '_a X.t -> '_a -> '_b option = <fun> val set1 : '_a X.t -> '_a -> '_b -> unit = <fun> # let (get2, set2) = access ();; val get2 : '_a X.t -> '_a -> '_b option = <fun> val set2 : '_a X.t -> '_a -> '_b -> unit = <fun> # 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! *) 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.) -- Alain ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-04 7:32 ` Alain Frisch @ 2013-04-04 18:16 ` Martin Jambon 0 siblings, 0 replies; 17+ messages in thread From: Martin Jambon @ 2013-04-04 18:16 UTC (permalink / raw) To: Alain Frisch; +Cc: Anthony Tavener, caml-list 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 = <abstr> > # let (get1, set1) = access ();; > val get1 : '_a X.t -> '_a -> '_b option = <fun> > val set1 : '_a X.t -> '_a -> '_b -> unit = <fun> > # let (get2, set2) = access ();; > val get2 : '_a X.t -> '_a -> '_b option = <fun> > val set2 : '_a X.t -> '_a -> '_b -> unit = <fun> > # 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. ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-04 0:45 [Caml-list] Heterogeneous dictionary Anthony Tavener 2013-04-04 1:29 ` Yaron Minsky 2013-04-04 6:19 ` Martin Jambon @ 2013-04-04 7:38 ` Raphaël Proust 2013-04-04 8:37 ` Anthony Tavener 2 siblings, 1 reply; 17+ messages in thread From: Raphaël Proust @ 2013-04-04 7:38 UTC (permalink / raw) To: Anthony Tavener; +Cc: caml-list On Thu, Apr 4, 2013 at 1:45 AM, Anthony Tavener <anthony.tavener@gmail.com> wrote: > […] And yet-another-solution, Ocsigen's Polytable: http://ocsigen.org/ocsigenserver/api/Polytables Cheers, -- ______________ Raphaël Proust ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-04 7:38 ` Raphaël Proust @ 2013-04-04 8:37 ` Anthony Tavener 2013-04-04 9:04 ` David House 0 siblings, 1 reply; 17+ messages in thread From: Anthony Tavener @ 2013-04-04 8:37 UTC (permalink / raw) To: Raphaël Proust; +Cc: caml-list [-- Attachment #1: Type: text/plain, Size: 1866 bytes --] Thank-you for the advice and pointers, folks... Well, the common problem is still the same one I've been struggling with: "creating keys", and having to access them. I can't create keys "type-free" in a common module. As I figured... having " modifier.ml" with a bunch of Key.create will have monomorphic types which can't be resolved since with no usage in that module to make the type concrete. I had a nagging feeling I'd need a "whole-program" compiler... Instead I'd have to create keys in modules where they are used... but then I might have a mess of keys like Wounds.recovery, Combat.resist_pain, ... the problem being that only a fraction of these keys actually make sense being associated to a particular module, and it gets confusing to know which (of several candidates) I decided to stash them into. This was the attraction to polymorphic variants (which I rarely use) -- they give a pre-ordained unique ID based on a simple name... no declaration, and no module prefixing, which seems important to me for this case. Note that I have a "database" of tables with different types (implemented by first-class modules!), and it works great for the bulk of my game-state, but each table is well-populated and heavily used in consistent manner. These modifiers though... they're a bit like ad-hoc message passing, where I can submit any message and anywhere else add a snippet of code to interpret it (not that I have any of that going on, otherwise it might hold the solution!). On Thu, Apr 4, 2013 at 1:38 AM, Raphaël Proust <raphlalou@gmail.com> wrote: > On Thu, Apr 4, 2013 at 1:45 AM, Anthony Tavener > <anthony.tavener@gmail.com> wrote: > > […] > > And yet-another-solution, Ocsigen's Polytable: > http://ocsigen.org/ocsigenserver/api/Polytables > > > Cheers, > -- > ______________ > Raphaël Proust > [-- Attachment #2: Type: text/html, Size: 2625 bytes --] ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-04 8:37 ` Anthony Tavener @ 2013-04-04 9:04 ` David House 2013-04-04 18:48 ` Anthony Tavener 0 siblings, 1 reply; 17+ messages in thread From: David House @ 2013-04-04 9:04 UTC (permalink / raw) To: Anthony Tavener; +Cc: Raphaël Proust, caml-list I don't quite understand the problem. Here's an example of how one might use univ_map: open Core.Std module Keys : sig val recovery : int Univ_map.Key.t val resist_pain : float Univ_map.Key.t end = struct let recovery = Univ_key.Key.create "recovery" Int.sexp_of_t let resist_pain = Univ_key.Key.create "resist_pain" Float.sexp_of_t end (In practice this might be two files: keys.ml with the implementation and keys.mli with the signature.) You can then add things as follows: let add map ~key ~data = Univ_map.add_exn map key data in let map = Univ_map.empty |> add ~key:Keys.recovery ~data:4 |> add ~key:Keys.resist_pain ~data:10. in ... On 4 April 2013 09:37, Anthony Tavener <anthony.tavener@gmail.com> wrote: > Thank-you for the advice and pointers, folks... > > Well, the common problem is still the same one I've been struggling with: > "creating keys", and having to access them. > > I can't create keys "type-free" in a common module. As I figured... having > "modifier.ml" with a bunch of Key.create will have monomorphic types which > can't be resolved since with no usage in that module to make the type > concrete. I had a nagging feeling I'd need a "whole-program" compiler... > > Instead I'd have to create keys in modules where they are used... but then I > might have a mess of keys like Wounds.recovery, Combat.resist_pain, ... the > problem being that only a fraction of these keys actually make sense being > associated to a particular module, and it gets confusing to know which (of > several candidates) I decided to stash them into. This was the attraction to > polymorphic variants (which I rarely use) -- they give a pre-ordained unique > ID based on a simple name... no declaration, and no module prefixing, which > seems important to me for this case. > > Note that I have a "database" of tables with different types (implemented by > first-class modules!), and it works great for the bulk of my game-state, but > each table is well-populated and heavily used in consistent manner. These > modifiers though... they're a bit like ad-hoc message passing, where I can > submit any message and anywhere else add a snippet of code to interpret it > (not that I have any of that going on, otherwise it might hold the > solution!). > > > > On Thu, Apr 4, 2013 at 1:38 AM, Raphaël Proust <raphlalou@gmail.com> wrote: >> >> On Thu, Apr 4, 2013 at 1:45 AM, Anthony Tavener >> <anthony.tavener@gmail.com> wrote: >> > […] >> >> And yet-another-solution, Ocsigen's Polytable: >> http://ocsigen.org/ocsigenserver/api/Polytables >> >> >> Cheers, >> -- >> ______________ >> Raphaël Proust > > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-04 9:04 ` David House @ 2013-04-04 18:48 ` Anthony Tavener 2013-04-05 16:37 ` Yaron Minsky 0 siblings, 1 reply; 17+ messages in thread From: Anthony Tavener @ 2013-04-04 18:48 UTC (permalink / raw) To: David House; +Cc: caml-list [-- Attachment #1: Type: text/plain, Size: 6006 bytes --] The problem here... module Keys : sig val recovery : int Univ_map.Key.t val resist_pain : float Univ_map.Key.t ... Would be that Keys would now have dependence on types spanning the codebase. Say these modifiers use Wounds.t, Fatigue.t, Ability.Score.t, ... There are a lot of types, which mostly have a limited scope (their own module and a few others). Wouldn't it be a problem to have all types brought into this one module, which every other module also becomes dependent on? Maybe it just feels like a problem but it's just an aesthetic -- Would you do this? A few hundred keys involving types from half the modules of the codebase? I'm trying to use these "modifiers" for code organisation -- declaring snippets of functionality (all of signature 'a -> 'a... to return modified input) in a lightweight manner, which can be applied elsewhere. For example, the Virtue module can add a lot of modifers to an entity (UID). Say one entity has virtues of Agility, Inspirational, and Sun-cyclic Magic, which each adding a few modifier functions keyed to various contexts (sun-cyclic magic: "Casting" is +3 while the sun is up). The associated modifiers would be picked up in code spread throughout the application. Rules for combat, spellcasting, even character dialog... Rather than having character dialog checking for "does he have this virtue, or that one? How about this ability? Spell-effects? Reputations? ..." I want to apply all appropriate/active modifiers for entity and situation which come from other rules. So, dialog code might have a current_value, then... eg. let modified_value = apply_modifiers_for_context entity `CharmRoll current_value There is a lot of work on my project which I've been avoiding because the direct approach is building hairy nests of checks which call out to code everywhere... effectively splitting logic between the point of origin of a rule, and the point of application of it. I'd rather declare the individual rules, each in one piece, and "magically" apply the aggregate of rules. In a way, what I'm looking for is having a grand-central lookup, which *trusts* that I'm using the right keys at the right time (where the keys are purely symbolic with no type info)... but it would be nice if once the whole program is compiled it could identify whether that trust was broken afterall. A bit of a pipe-dream, and maybe even flawed logic. :) It might sound like I'm being too picky or even whiney... that declaring keys throughout the code is unacceptable, or having a file dependent on all types is problematic... maybe I am? The first seems disorganised and adds a mental burden to deciding and knowing where keys live; the second is a problem, isn't it? Dynamic languages can do what I want at the cost of typesafety. So I might just prefer to make that same tradeoff for one mechanism in my code... "famous last words"? I hope not. I hope it just works and I don't have nightmares about lurking segfaults. :) On Thu, Apr 4, 2013 at 3:04 AM, David House <dhouse@janestreet.com> wrote: > I don't quite understand the problem. Here's an example of how one > might use univ_map: > > open Core.Std > > module Keys : sig > val recovery : int Univ_map.Key.t > val resist_pain : float Univ_map.Key.t > end = struct > let recovery = Univ_key.Key.create "recovery" Int.sexp_of_t > let resist_pain = Univ_key.Key.create "resist_pain" Float.sexp_of_t > end > > (In practice this might be two files: keys.ml with the implementation > and keys.mli with the signature.) You can then add things as follows: > > let add map ~key ~data = Univ_map.add_exn map key data in > let map = > Univ_map.empty > |> add ~key:Keys.recovery ~data:4 > |> add ~key:Keys.resist_pain ~data:10. > in > ... > > On 4 April 2013 09:37, Anthony Tavener <anthony.tavener@gmail.com> wrote: > > Thank-you for the advice and pointers, folks... > > > > Well, the common problem is still the same one I've been struggling with: > > "creating keys", and having to access them. > > > > I can't create keys "type-free" in a common module. As I figured... > having > > "modifier.ml" with a bunch of Key.create will have monomorphic types > which > > can't be resolved since with no usage in that module to make the type > > concrete. I had a nagging feeling I'd need a "whole-program" compiler... > > > > Instead I'd have to create keys in modules where they are used... but > then I > > might have a mess of keys like Wounds.recovery, Combat.resist_pain, ... > the > > problem being that only a fraction of these keys actually make sense > being > > associated to a particular module, and it gets confusing to know which > (of > > several candidates) I decided to stash them into. This was the > attraction to > > polymorphic variants (which I rarely use) -- they give a pre-ordained > unique > > ID based on a simple name... no declaration, and no module prefixing, > which > > seems important to me for this case. > > > > Note that I have a "database" of tables with different types > (implemented by > > first-class modules!), and it works great for the bulk of my game-state, > but > > each table is well-populated and heavily used in consistent manner. These > > modifiers though... they're a bit like ad-hoc message passing, where I > can > > submit any message and anywhere else add a snippet of code to interpret > it > > (not that I have any of that going on, otherwise it might hold the > > solution!). > > > > > > > > On Thu, Apr 4, 2013 at 1:38 AM, Raphaël Proust <raphlalou@gmail.com> > wrote: > >> > >> On Thu, Apr 4, 2013 at 1:45 AM, Anthony Tavener > >> <anthony.tavener@gmail.com> wrote: > >> > […] > >> > >> And yet-another-solution, Ocsigen's Polytable: > >> http://ocsigen.org/ocsigenserver/api/Polytables > >> > >> > >> Cheers, > >> -- > >> ______________ > >> Raphaël Proust > > > > > [-- Attachment #2: Type: text/html, Size: 7792 bytes --] ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-04 18:48 ` Anthony Tavener @ 2013-04-05 16:37 ` Yaron Minsky 2013-04-05 18:27 ` Anthony Tavener 0 siblings, 1 reply; 17+ messages in thread From: Yaron Minsky @ 2013-04-05 16:37 UTC (permalink / raw) To: Anthony Tavener; +Cc: David House, caml-list On Thu, Apr 4, 2013 at 2:48 PM, Anthony Tavener <anthony.tavener@gmail.com> wrote: > The problem here... > > module Keys : sig > val recovery : int Univ_map.Key.t > val resist_pain : float Univ_map.Key.t > ... > > Would be that Keys would now have dependence on types spanning the codebase. Sure, but there's nothing that requires these to be stored together. They can be scattered to the four winds, and yet all used to keep things in the same Univ_map.t. It's honestly very much like using a dynamic language, where you get to declare new items at will that can be cast in and out from a universal type. It's probably worth reading over the Univ module in Core as well, since Univ_map is built on it, and if Univ_map doesn't quite do what you want, you can probably built what you need precisely on top of Univ. > Say these modifiers use Wounds.t, Fatigue.t, Ability.Score.t, ... There are > a > lot of types, which mostly have a limited scope (their own module and a few > others). Wouldn't it be a problem to have all types brought into this one > module, which every other module also becomes dependent on? Maybe it just > feels like a problem but it's just an aesthetic -- Would you do this? A few > hundred keys involving types from half the modules of the codebase? > > > I'm trying to use these "modifiers" for code organisation -- declaring > snippets of functionality (all of signature 'a -> 'a... to return modified > input) in a lightweight manner, which can be applied elsewhere. > > For example, the Virtue module can add a lot of modifers to an entity (UID). > Say one entity has virtues of Agility, Inspirational, and Sun-cyclic Magic, > which each adding a few modifier functions keyed to various contexts > (sun-cyclic magic: "Casting" is +3 while the sun is up). The associated > modifiers would be picked up in code spread throughout the application. > Rules > for combat, spellcasting, even character dialog... > > Rather than having character dialog checking for "does he have this virtue, > or > that one? How about this ability? Spell-effects? Reputations? ..." I want to > apply all appropriate/active modifiers for entity and situation which come > from other rules. So, dialog code might have a current_value, then... > eg. > let modified_value = apply_modifiers_for_context entity `CharmRoll > current_value > > There is a lot of work on my project which I've been avoiding because the > direct approach is building hairy nests of checks which call out to code > everywhere... effectively splitting logic between the point of origin of a > rule, and the point of application of it. I'd rather declare the individual > rules, each in one piece, and "magically" apply the aggregate of rules. > > > In a way, what I'm looking for is having a grand-central lookup, which > *trusts* that I'm using the right keys at the right time (where the keys are > purely symbolic with no type info)... but it would be nice if once the whole > program is compiled it could identify whether that trust was broken > afterall. > A bit of a pipe-dream, and maybe even flawed logic. :) > > It might sound like I'm being too picky or even whiney... that declaring > keys > throughout the code is unacceptable, or having a file dependent on all types > is problematic... maybe I am? The first seems disorganised and adds a > mental burden to deciding and knowing where keys live; the second is a > problem, isn't it? > > Dynamic languages can do what I want at the cost of typesafety. So I might > just prefer to make that same tradeoff for one mechanism in my code... > "famous > last words"? I hope not. I hope it just works and I don't have nightmares > about lurking segfaults. :) > > > On Thu, Apr 4, 2013 at 3:04 AM, David House <dhouse@janestreet.com> wrote: >> >> I don't quite understand the problem. Here's an example of how one >> might use univ_map: >> >> open Core.Std >> >> module Keys : sig >> val recovery : int Univ_map.Key.t >> val resist_pain : float Univ_map.Key.t >> end = struct >> let recovery = Univ_key.Key.create "recovery" Int.sexp_of_t >> let resist_pain = Univ_key.Key.create "resist_pain" Float.sexp_of_t >> end >> >> (In practice this might be two files: keys.ml with the implementation >> and keys.mli with the signature.) You can then add things as follows: >> >> let add map ~key ~data = Univ_map.add_exn map key data in >> let map = >> Univ_map.empty >> |> add ~key:Keys.recovery ~data:4 >> |> add ~key:Keys.resist_pain ~data:10. >> in >> ... >> >> On 4 April 2013 09:37, Anthony Tavener <anthony.tavener@gmail.com> wrote: >> > Thank-you for the advice and pointers, folks... >> > >> > Well, the common problem is still the same one I've been struggling >> > with: >> > "creating keys", and having to access them. >> > >> > I can't create keys "type-free" in a common module. As I figured... >> > having >> > "modifier.ml" with a bunch of Key.create will have monomorphic types >> > which >> > can't be resolved since with no usage in that module to make the type >> > concrete. I had a nagging feeling I'd need a "whole-program" compiler... >> > >> > Instead I'd have to create keys in modules where they are used... but >> > then I >> > might have a mess of keys like Wounds.recovery, Combat.resist_pain, ... >> > the >> > problem being that only a fraction of these keys actually make sense >> > being >> > associated to a particular module, and it gets confusing to know which >> > (of >> > several candidates) I decided to stash them into. This was the >> > attraction to >> > polymorphic variants (which I rarely use) -- they give a pre-ordained >> > unique >> > ID based on a simple name... no declaration, and no module prefixing, >> > which >> > seems important to me for this case. >> > >> > Note that I have a "database" of tables with different types >> > (implemented by >> > first-class modules!), and it works great for the bulk of my game-state, >> > but >> > each table is well-populated and heavily used in consistent manner. >> > These >> > modifiers though... they're a bit like ad-hoc message passing, where I >> > can >> > submit any message and anywhere else add a snippet of code to interpret >> > it >> > (not that I have any of that going on, otherwise it might hold the >> > solution!). >> > >> > >> > >> > On Thu, Apr 4, 2013 at 1:38 AM, Raphaël Proust <raphlalou@gmail.com> >> > wrote: >> >> >> >> On Thu, Apr 4, 2013 at 1:45 AM, Anthony Tavener >> >> <anthony.tavener@gmail.com> wrote: >> >> > […] >> >> >> >> And yet-another-solution, Ocsigen's Polytable: >> >> http://ocsigen.org/ocsigenserver/api/Polytables >> >> >> >> >> >> Cheers, >> >> -- >> >> ______________ >> >> Raphaël Proust >> > >> > > > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-05 16:37 ` Yaron Minsky @ 2013-04-05 18:27 ` Anthony Tavener 2013-04-05 18:51 ` Yaron Minsky 0 siblings, 1 reply; 17+ messages in thread From: Anthony Tavener @ 2013-04-05 18:27 UTC (permalink / raw) To: Yaron Minsky; +Cc: caml-list [-- Attachment #1: Type: text/plain, Size: 10488 bytes --] > Sure, but there's nothing that requires these to be stored together. > They can be scattered to the four winds, and yet all used to keep > things in the same Univ_map.t. I was working with a universal type, but that was exactly the problem I encountered: I need to create the keys and hold them somewhere. My original post might have been unclear, sorry. This is what I meant by having to pre-create the inj/proj pair and house them somewhere... where a single file would create a bottleneck of types, or scattered becomes a "mess". Scattered would prove confusing I think... with hundreds of keys that have no sensible "home" module. Well, some might make sense, but most of these keys are interstitial -- between modules, somewhat like messages. I was trying to map out where various keys would live and it wasn't obvious and would continue to be unclear when I wanted to use a key. There would be cognitive burden of deciding home modules for things which don't fit into the modular namespace. So, a universal type with "keys" does solve the problem, for some definition of solved. My concern is that the solution is too cumbersome and confusing in this use-case. Very much like arguments for polymorphic variants versus variants. Unfortunately I don't see any way to "embed type" in a polymorphic variant (I did check whether hashes could match between different function implementations of same type-signature... which was kind of silly because I'm pretty sure the function address would be used in the hashvalue). Although I imagine types do have hashvalues at some point in the compiler! Another (failed?) possibility I mentioned as a pipe-dream: if "keys" could be left open "to trust", and then verified that all trusted usages lined up at link-time. After you're first reply (Yaron), I thought for a moment that monomorphic types might work like that -- resolved at link time. Of course, they're not, as a test quickly confirmed. :( But... that's why I asked the wise body of OCamlers if you have ideas -- I don't have a complete grasp of the language and might be missing something or have a false assumption. It's a complex type-system! I like it a lot though. Thinking more about this, and formulating these replies, I've realized there's a common idiom in game development -- and maybe software in general? It's use of string-hashes to create flexible associations... from code to code, or code to data (I think that's how it starts... you need to share "IDs" between code and data -- and then the technique spreads). Messages, names of special points in artwork (eg "primary-grip"), effects, things accessible by script... But with such extensive usage, you need tools to verify correct usage as much as possible. C, of course, provides few guarantees of anything, so you write your own tools. OCaml is tantalizing with its rich typesystem -- and polymorphic variants are like built-in string-hash support. So I kept trying to figure out a way to get OCaml to do the heavy lifting... for idioms I might be unreasonably stuck on. ;) On Fri, Apr 5, 2013 at 10:37 AM, Yaron Minsky <yminsky@janestreet.com>wrote: > On Thu, Apr 4, 2013 at 2:48 PM, Anthony Tavener > <anthony.tavener@gmail.com> wrote: > > The problem here... > > > > module Keys : sig > > val recovery : int Univ_map.Key.t > > val resist_pain : float Univ_map.Key.t > > ... > > > > Would be that Keys would now have dependence on types spanning the > codebase. > > Sure, but there's nothing that requires these to be stored together. > They can be scattered to the four winds, and yet all used to keep > things in the same Univ_map.t. > > It's honestly very much like using a dynamic language, where you get > to declare new items at will that can be cast in and out from a > universal type. > > It's probably worth reading over the Univ module in Core as well, > since Univ_map is built on it, and if Univ_map doesn't quite do what > you want, you can probably built what you need precisely on top of > Univ. > > > Say these modifiers use Wounds.t, Fatigue.t, Ability.Score.t, ... There > are > > a > > lot of types, which mostly have a limited scope (their own module and a > few > > others). Wouldn't it be a problem to have all types brought into this one > > module, which every other module also becomes dependent on? Maybe it just > > feels like a problem but it's just an aesthetic -- Would you do this? A > few > > hundred keys involving types from half the modules of the codebase? > > > > > > I'm trying to use these "modifiers" for code organisation -- declaring > > snippets of functionality (all of signature 'a -> 'a... to return > modified > > input) in a lightweight manner, which can be applied elsewhere. > > > > For example, the Virtue module can add a lot of modifers to an entity > (UID). > > Say one entity has virtues of Agility, Inspirational, and Sun-cyclic > Magic, > > which each adding a few modifier functions keyed to various contexts > > (sun-cyclic magic: "Casting" is +3 while the sun is up). The associated > > modifiers would be picked up in code spread throughout the application. > > Rules > > for combat, spellcasting, even character dialog... > > > > Rather than having character dialog checking for "does he have this > virtue, > > or > > that one? How about this ability? Spell-effects? Reputations? ..." I > want to > > apply all appropriate/active modifiers for entity and situation which > come > > from other rules. So, dialog code might have a current_value, then... > > eg. > > let modified_value = apply_modifiers_for_context entity `CharmRoll > > current_value > > > > There is a lot of work on my project which I've been avoiding because the > > direct approach is building hairy nests of checks which call out to code > > everywhere... effectively splitting logic between the point of origin of > a > > rule, and the point of application of it. I'd rather declare the > individual > > rules, each in one piece, and "magically" apply the aggregate of rules. > > > > > > In a way, what I'm looking for is having a grand-central lookup, which > > *trusts* that I'm using the right keys at the right time (where the keys > are > > purely symbolic with no type info)... but it would be nice if once the > whole > > program is compiled it could identify whether that trust was broken > > afterall. > > A bit of a pipe-dream, and maybe even flawed logic. :) > > > > It might sound like I'm being too picky or even whiney... that declaring > > keys > > throughout the code is unacceptable, or having a file dependent on all > types > > is problematic... maybe I am? The first seems disorganised and adds a > > mental burden to deciding and knowing where keys live; the second is a > > problem, isn't it? > > > > Dynamic languages can do what I want at the cost of typesafety. So I > might > > just prefer to make that same tradeoff for one mechanism in my code... > > "famous > > last words"? I hope not. I hope it just works and I don't have nightmares > > about lurking segfaults. :) > > > > > > On Thu, Apr 4, 2013 at 3:04 AM, David House <dhouse@janestreet.com> > wrote: > >> > >> I don't quite understand the problem. Here's an example of how one > >> might use univ_map: > >> > >> open Core.Std > >> > >> module Keys : sig > >> val recovery : int Univ_map.Key.t > >> val resist_pain : float Univ_map.Key.t > >> end = struct > >> let recovery = Univ_key.Key.create "recovery" Int.sexp_of_t > >> let resist_pain = Univ_key.Key.create "resist_pain" Float.sexp_of_t > >> end > >> > >> (In practice this might be two files: keys.ml with the implementation > >> and keys.mli with the signature.) You can then add things as follows: > >> > >> let add map ~key ~data = Univ_map.add_exn map key data in > >> let map = > >> Univ_map.empty > >> |> add ~key:Keys.recovery ~data:4 > >> |> add ~key:Keys.resist_pain ~data:10. > >> in > >> ... > >> > >> On 4 April 2013 09:37, Anthony Tavener <anthony.tavener@gmail.com> > wrote: > >> > Thank-you for the advice and pointers, folks... > >> > > >> > Well, the common problem is still the same one I've been struggling > >> > with: > >> > "creating keys", and having to access them. > >> > > >> > I can't create keys "type-free" in a common module. As I figured... > >> > having > >> > "modifier.ml" with a bunch of Key.create will have monomorphic types > >> > which > >> > can't be resolved since with no usage in that module to make the type > >> > concrete. I had a nagging feeling I'd need a "whole-program" > compiler... > >> > > >> > Instead I'd have to create keys in modules where they are used... but > >> > then I > >> > might have a mess of keys like Wounds.recovery, Combat.resist_pain, > ... > >> > the > >> > problem being that only a fraction of these keys actually make sense > >> > being > >> > associated to a particular module, and it gets confusing to know which > >> > (of > >> > several candidates) I decided to stash them into. This was the > >> > attraction to > >> > polymorphic variants (which I rarely use) -- they give a pre-ordained > >> > unique > >> > ID based on a simple name... no declaration, and no module prefixing, > >> > which > >> > seems important to me for this case. > >> > > >> > Note that I have a "database" of tables with different types > >> > (implemented by > >> > first-class modules!), and it works great for the bulk of my > game-state, > >> > but > >> > each table is well-populated and heavily used in consistent manner. > >> > These > >> > modifiers though... they're a bit like ad-hoc message passing, where I > >> > can > >> > submit any message and anywhere else add a snippet of code to > interpret > >> > it > >> > (not that I have any of that going on, otherwise it might hold the > >> > solution!). > >> > > >> > > >> > > >> > On Thu, Apr 4, 2013 at 1:38 AM, Raphaël Proust <raphlalou@gmail.com> > >> > wrote: > >> >> > >> >> On Thu, Apr 4, 2013 at 1:45 AM, Anthony Tavener > >> >> <anthony.tavener@gmail.com> wrote: > >> >> > […] > >> >> > >> >> And yet-another-solution, Ocsigen's Polytable: > >> >> http://ocsigen.org/ocsigenserver/api/Polytables > >> >> > >> >> > >> >> Cheers, > >> >> -- > >> >> ______________ > >> >> Raphaël Proust > >> > > >> > > > > > > [-- Attachment #2: Type: text/html, Size: 14325 bytes --] ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-05 18:27 ` Anthony Tavener @ 2013-04-05 18:51 ` Yaron Minsky 2013-04-05 19:55 ` Anthony Tavener 0 siblings, 1 reply; 17+ messages in thread From: Yaron Minsky @ 2013-04-05 18:51 UTC (permalink / raw) To: Anthony Tavener; +Cc: caml-list On Fri, Apr 5, 2013 at 2:27 PM, Anthony Tavener <anthony.tavener@gmail.com> wrote: >> Sure, but there's nothing that requires these to be stored together. >> They can be scattered to the four winds, and yet all used to keep >> things in the same Univ_map.t. > > I was working with a universal type, but that was exactly the > problem I encountered: I need to create the keys and hold them > somewhere. My original post might have been unclear, sorry. This is > what I meant by having to pre-create the inj/proj pair and house > them somewhere... where a single file would create a bottleneck of > types, or scattered becomes a "mess". Scattered would prove > confusing I think... with hundreds of keys that have no sensible > "home" module. Well, some might make sense, but most of these keys > are interstitial -- between modules, somewhat like messages. I was > trying to map out where various keys would live and it wasn't > obvious and would continue to be unclear when I wanted to use a > key. There would be cognitive burden of deciding home modules for > things which don't fit into the modular namespace. My experience with this kind of design is that this isn't that much of a problem. You can create multiple modules that house keys for different concerns. Indeed, in such designs I've historically created modules with typeful interfaces that back-end on such keys, but simply expose a typeful way of getting access to the properties in question, which I think turns out quite prettily. > So, a universal type with "keys" does solve the problem, for some > definition of solved. My concern is that the solution is too > cumbersome and confusing in this use-case. Very much like arguments > for polymorphic variants versus variants. Unfortunately I don't see > any way to "embed type" in a polymorphic variant (I did check > whether hashes could match between different function > implementations of same type-signature... which was kind of silly > because I'm pretty sure the function address would be used in the > hashvalue). Although I imagine types do have hashvalues at some > point in the compiler! Again, If you create wrapper modules for accessing and working with these properties, I think everything will work smoothly. Indeed, if you have a module T for a given type of thing to be stored in one of these maps, you can build your typeful accessors for T.t into module T. You just need to follow the design pattern of having one module per type you define. > Another (failed?) possibility I mentioned as a pipe-dream: if "keys" > could be left open "to trust", and then verified that all trusted > usages lined up at link-time. After you're first reply (Yaron), I > thought for a moment that monomorphic types might work like that -- > resolved at link time. Of course, they're not, as a test quickly > confirmed. :( But... that's why I asked the wise body of OCamlers if > you have ideas -- I don't have a complete grasp of the language and > might be missing something or have a false assumption. It's a > complex type-system! I like it a lot though. > > Thinking more about this, and formulating these replies, I've > realized there's a common idiom in game development -- and maybe > software in general? It's use of string-hashes to create flexible > associations... from code to code, or code to data (I think that's > how it starts... you need to share "IDs" between code and data -- > and then the technique spreads). Messages, names of special points > in artwork (eg "primary-grip"), effects, things accessible by > script... But with such extensive usage, you need tools to verify > correct usage as much as possible. C, of course, provides few > guarantees of anything, so you write your own tools. OCaml is > tantalizing with its rich typesystem -- and polymorphic variants are > like built-in string-hash support. So I kept trying to figure out a > way to get OCaml to do the heavy lifting... for idioms I might be > unreasonably stuck on. ;) Maybe. My intuition is that the design pattern you're talking about is a good one, and really can be made to work well in OCaml. y > On Fri, Apr 5, 2013 at 10:37 AM, Yaron Minsky <yminsky@janestreet.com> > wrote: >> >> On Thu, Apr 4, 2013 at 2:48 PM, Anthony Tavener >> <anthony.tavener@gmail.com> wrote: >> > The problem here... >> > >> > module Keys : sig >> > val recovery : int Univ_map.Key.t >> > val resist_pain : float Univ_map.Key.t >> > ... >> > >> > Would be that Keys would now have dependence on types spanning the >> > codebase. >> >> Sure, but there's nothing that requires these to be stored together. >> They can be scattered to the four winds, and yet all used to keep >> things in the same Univ_map.t. >> >> It's honestly very much like using a dynamic language, where you get >> to declare new items at will that can be cast in and out from a >> universal type. >> >> It's probably worth reading over the Univ module in Core as well, >> since Univ_map is built on it, and if Univ_map doesn't quite do what >> you want, you can probably built what you need precisely on top of >> Univ. >> >> > Say these modifiers use Wounds.t, Fatigue.t, Ability.Score.t, ... There >> > are >> > a >> > lot of types, which mostly have a limited scope (their own module and a >> > few >> > others). Wouldn't it be a problem to have all types brought into this >> > one >> > module, which every other module also becomes dependent on? Maybe it >> > just >> > feels like a problem but it's just an aesthetic -- Would you do this? A >> > few >> > hundred keys involving types from half the modules of the codebase? >> > >> > >> > I'm trying to use these "modifiers" for code organisation -- declaring >> > snippets of functionality (all of signature 'a -> 'a... to return >> > modified >> > input) in a lightweight manner, which can be applied elsewhere. >> > >> > For example, the Virtue module can add a lot of modifers to an entity >> > (UID). >> > Say one entity has virtues of Agility, Inspirational, and Sun-cyclic >> > Magic, >> > which each adding a few modifier functions keyed to various contexts >> > (sun-cyclic magic: "Casting" is +3 while the sun is up). The associated >> > modifiers would be picked up in code spread throughout the application. >> > Rules >> > for combat, spellcasting, even character dialog... >> > >> > Rather than having character dialog checking for "does he have this >> > virtue, >> > or >> > that one? How about this ability? Spell-effects? Reputations? ..." I >> > want to >> > apply all appropriate/active modifiers for entity and situation which >> > come >> > from other rules. So, dialog code might have a current_value, then... >> > eg. >> > let modified_value = apply_modifiers_for_context entity `CharmRoll >> > current_value >> > >> > There is a lot of work on my project which I've been avoiding because >> > the >> > direct approach is building hairy nests of checks which call out to code >> > everywhere... effectively splitting logic between the point of origin of >> > a >> > rule, and the point of application of it. I'd rather declare the >> > individual >> > rules, each in one piece, and "magically" apply the aggregate of rules. >> > >> > >> > In a way, what I'm looking for is having a grand-central lookup, which >> > *trusts* that I'm using the right keys at the right time (where the keys >> > are >> > purely symbolic with no type info)... but it would be nice if once the >> > whole >> > program is compiled it could identify whether that trust was broken >> > afterall. >> > A bit of a pipe-dream, and maybe even flawed logic. :) >> > >> > It might sound like I'm being too picky or even whiney... that declaring >> > keys >> > throughout the code is unacceptable, or having a file dependent on all >> > types >> > is problematic... maybe I am? The first seems disorganised and adds a >> > mental burden to deciding and knowing where keys live; the second is a >> > problem, isn't it? >> > >> > Dynamic languages can do what I want at the cost of typesafety. So I >> > might >> > just prefer to make that same tradeoff for one mechanism in my code... >> > "famous >> > last words"? I hope not. I hope it just works and I don't have >> > nightmares >> > about lurking segfaults. :) >> > >> > >> > On Thu, Apr 4, 2013 at 3:04 AM, David House <dhouse@janestreet.com> >> > wrote: >> >> >> >> I don't quite understand the problem. Here's an example of how one >> >> might use univ_map: >> >> >> >> open Core.Std >> >> >> >> module Keys : sig >> >> val recovery : int Univ_map.Key.t >> >> val resist_pain : float Univ_map.Key.t >> >> end = struct >> >> let recovery = Univ_key.Key.create "recovery" Int.sexp_of_t >> >> let resist_pain = Univ_key.Key.create "resist_pain" Float.sexp_of_t >> >> end >> >> >> >> (In practice this might be two files: keys.ml with the implementation >> >> and keys.mli with the signature.) You can then add things as follows: >> >> >> >> let add map ~key ~data = Univ_map.add_exn map key data in >> >> let map = >> >> Univ_map.empty >> >> |> add ~key:Keys.recovery ~data:4 >> >> |> add ~key:Keys.resist_pain ~data:10. >> >> in >> >> ... >> >> >> >> On 4 April 2013 09:37, Anthony Tavener <anthony.tavener@gmail.com> >> >> wrote: >> >> > Thank-you for the advice and pointers, folks... >> >> > >> >> > Well, the common problem is still the same one I've been struggling >> >> > with: >> >> > "creating keys", and having to access them. >> >> > >> >> > I can't create keys "type-free" in a common module. As I figured... >> >> > having >> >> > "modifier.ml" with a bunch of Key.create will have monomorphic types >> >> > which >> >> > can't be resolved since with no usage in that module to make the type >> >> > concrete. I had a nagging feeling I'd need a "whole-program" >> >> > compiler... >> >> > >> >> > Instead I'd have to create keys in modules where they are used... but >> >> > then I >> >> > might have a mess of keys like Wounds.recovery, Combat.resist_pain, >> >> > ... >> >> > the >> >> > problem being that only a fraction of these keys actually make sense >> >> > being >> >> > associated to a particular module, and it gets confusing to know >> >> > which >> >> > (of >> >> > several candidates) I decided to stash them into. This was the >> >> > attraction to >> >> > polymorphic variants (which I rarely use) -- they give a pre-ordained >> >> > unique >> >> > ID based on a simple name... no declaration, and no module prefixing, >> >> > which >> >> > seems important to me for this case. >> >> > >> >> > Note that I have a "database" of tables with different types >> >> > (implemented by >> >> > first-class modules!), and it works great for the bulk of my >> >> > game-state, >> >> > but >> >> > each table is well-populated and heavily used in consistent manner. >> >> > These >> >> > modifiers though... they're a bit like ad-hoc message passing, where >> >> > I >> >> > can >> >> > submit any message and anywhere else add a snippet of code to >> >> > interpret >> >> > it >> >> > (not that I have any of that going on, otherwise it might hold the >> >> > solution!). >> >> > >> >> > >> >> > >> >> > On Thu, Apr 4, 2013 at 1:38 AM, Raphaël Proust <raphlalou@gmail.com> >> >> > wrote: >> >> >> >> >> >> On Thu, Apr 4, 2013 at 1:45 AM, Anthony Tavener >> >> >> <anthony.tavener@gmail.com> wrote: >> >> >> > […] >> >> >> >> >> >> And yet-another-solution, Ocsigen's Polytable: >> >> >> http://ocsigen.org/ocsigenserver/api/Polytables >> >> >> >> >> >> >> >> >> Cheers, >> >> >> -- >> >> >> ______________ >> >> >> Raphaël Proust >> >> > >> >> > >> > >> > > > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-05 18:51 ` Yaron Minsky @ 2013-04-05 19:55 ` Anthony Tavener 2013-04-05 20:03 ` Yaron Minsky 0 siblings, 1 reply; 17+ messages in thread From: Anthony Tavener @ 2013-04-05 19:55 UTC (permalink / raw) To: Yaron Minsky; +Cc: caml-list [-- Attachment #1: Type: text/plain, Size: 13469 bytes --] Hmm... that is some food for thought. I wasn't really considering the creation of modules which might only hold a few keys. Sometimes we overlook simple things. :) You've convinced me enough to work with it and see how it fits -- it sounds like you've had similar cases, and turning out "prettily" is appealing. I do have a pattern of Module.t. I remember thinking that was an odd idiom, at first, and used an "appropriate" full name. Once I really started using modules, and especially functors, I saw the light. Is there a design patterns for OCaml, somewhere? :) Thank-you for wading through my walls of text and still offering advice, Yaron! On Fri, Apr 5, 2013 at 12:51 PM, Yaron Minsky <yminsky@janestreet.com>wrote: > On Fri, Apr 5, 2013 at 2:27 PM, Anthony Tavener > <anthony.tavener@gmail.com> wrote: > >> Sure, but there's nothing that requires these to be stored together. > >> They can be scattered to the four winds, and yet all used to keep > >> things in the same Univ_map.t. > > > > I was working with a universal type, but that was exactly the > > problem I encountered: I need to create the keys and hold them > > somewhere. My original post might have been unclear, sorry. This is > > what I meant by having to pre-create the inj/proj pair and house > > them somewhere... where a single file would create a bottleneck of > > types, or scattered becomes a "mess". Scattered would prove > > confusing I think... with hundreds of keys that have no sensible > > "home" module. Well, some might make sense, but most of these keys > > are interstitial -- between modules, somewhat like messages. I was > > trying to map out where various keys would live and it wasn't > > obvious and would continue to be unclear when I wanted to use a > > key. There would be cognitive burden of deciding home modules for > > things which don't fit into the modular namespace. > > My experience with this kind of design is that this isn't that much of > a problem. You can create multiple modules that house keys for > different concerns. Indeed, in such designs I've historically created > modules with typeful interfaces that back-end on such keys, but simply > expose a typeful way of getting access to the properties in question, > which I think turns out quite prettily. > > > So, a universal type with "keys" does solve the problem, for some > > definition of solved. My concern is that the solution is too > > cumbersome and confusing in this use-case. Very much like arguments > > for polymorphic variants versus variants. Unfortunately I don't see > > any way to "embed type" in a polymorphic variant (I did check > > whether hashes could match between different function > > implementations of same type-signature... which was kind of silly > > because I'm pretty sure the function address would be used in the > > hashvalue). Although I imagine types do have hashvalues at some > > point in the compiler! > > Again, If you create wrapper modules for accessing and working with > these properties, I think everything will work smoothly. Indeed, if > you have a module T for a given type of thing to be stored in one of > these maps, you can build your typeful accessors for T.t into module > T. You just need to follow the design pattern of having one module > per type you define. > > > Another (failed?) possibility I mentioned as a pipe-dream: if "keys" > > could be left open "to trust", and then verified that all trusted > > usages lined up at link-time. After you're first reply (Yaron), I > > thought for a moment that monomorphic types might work like that -- > > resolved at link time. Of course, they're not, as a test quickly > > confirmed. :( But... that's why I asked the wise body of OCamlers if > > you have ideas -- I don't have a complete grasp of the language and > > might be missing something or have a false assumption. It's a > > complex type-system! I like it a lot though. > > > > Thinking more about this, and formulating these replies, I've > > realized there's a common idiom in game development -- and maybe > > software in general? It's use of string-hashes to create flexible > > associations... from code to code, or code to data (I think that's > > how it starts... you need to share "IDs" between code and data -- > > and then the technique spreads). Messages, names of special points > > in artwork (eg "primary-grip"), effects, things accessible by > > script... But with such extensive usage, you need tools to verify > > correct usage as much as possible. C, of course, provides few > > guarantees of anything, so you write your own tools. OCaml is > > tantalizing with its rich typesystem -- and polymorphic variants are > > like built-in string-hash support. So I kept trying to figure out a > > way to get OCaml to do the heavy lifting... for idioms I might be > > unreasonably stuck on. ;) > > Maybe. My intuition is that the design pattern you're talking about > is a good one, and really can be made to work well in OCaml. > > y > > > On Fri, Apr 5, 2013 at 10:37 AM, Yaron Minsky <yminsky@janestreet.com> > > wrote: > >> > >> On Thu, Apr 4, 2013 at 2:48 PM, Anthony Tavener > >> <anthony.tavener@gmail.com> wrote: > >> > The problem here... > >> > > >> > module Keys : sig > >> > val recovery : int Univ_map.Key.t > >> > val resist_pain : float Univ_map.Key.t > >> > ... > >> > > >> > Would be that Keys would now have dependence on types spanning the > >> > codebase. > >> > >> Sure, but there's nothing that requires these to be stored together. > >> They can be scattered to the four winds, and yet all used to keep > >> things in the same Univ_map.t. > >> > >> It's honestly very much like using a dynamic language, where you get > >> to declare new items at will that can be cast in and out from a > >> universal type. > >> > >> It's probably worth reading over the Univ module in Core as well, > >> since Univ_map is built on it, and if Univ_map doesn't quite do what > >> you want, you can probably built what you need precisely on top of > >> Univ. > >> > >> > Say these modifiers use Wounds.t, Fatigue.t, Ability.Score.t, ... > There > >> > are > >> > a > >> > lot of types, which mostly have a limited scope (their own module and > a > >> > few > >> > others). Wouldn't it be a problem to have all types brought into this > >> > one > >> > module, which every other module also becomes dependent on? Maybe it > >> > just > >> > feels like a problem but it's just an aesthetic -- Would you do this? > A > >> > few > >> > hundred keys involving types from half the modules of the codebase? > >> > > >> > > >> > I'm trying to use these "modifiers" for code organisation -- declaring > >> > snippets of functionality (all of signature 'a -> 'a... to return > >> > modified > >> > input) in a lightweight manner, which can be applied elsewhere. > >> > > >> > For example, the Virtue module can add a lot of modifers to an entity > >> > (UID). > >> > Say one entity has virtues of Agility, Inspirational, and Sun-cyclic > >> > Magic, > >> > which each adding a few modifier functions keyed to various contexts > >> > (sun-cyclic magic: "Casting" is +3 while the sun is up). The > associated > >> > modifiers would be picked up in code spread throughout the > application. > >> > Rules > >> > for combat, spellcasting, even character dialog... > >> > > >> > Rather than having character dialog checking for "does he have this > >> > virtue, > >> > or > >> > that one? How about this ability? Spell-effects? Reputations? ..." I > >> > want to > >> > apply all appropriate/active modifiers for entity and situation which > >> > come > >> > from other rules. So, dialog code might have a current_value, then... > >> > eg. > >> > let modified_value = apply_modifiers_for_context entity `CharmRoll > >> > current_value > >> > > >> > There is a lot of work on my project which I've been avoiding because > >> > the > >> > direct approach is building hairy nests of checks which call out to > code > >> > everywhere... effectively splitting logic between the point of origin > of > >> > a > >> > rule, and the point of application of it. I'd rather declare the > >> > individual > >> > rules, each in one piece, and "magically" apply the aggregate of > rules. > >> > > >> > > >> > In a way, what I'm looking for is having a grand-central lookup, which > >> > *trusts* that I'm using the right keys at the right time (where the > keys > >> > are > >> > purely symbolic with no type info)... but it would be nice if once the > >> > whole > >> > program is compiled it could identify whether that trust was broken > >> > afterall. > >> > A bit of a pipe-dream, and maybe even flawed logic. :) > >> > > >> > It might sound like I'm being too picky or even whiney... that > declaring > >> > keys > >> > throughout the code is unacceptable, or having a file dependent on all > >> > types > >> > is problematic... maybe I am? The first seems disorganised and adds a > >> > mental burden to deciding and knowing where keys live; the second is a > >> > problem, isn't it? > >> > > >> > Dynamic languages can do what I want at the cost of typesafety. So I > >> > might > >> > just prefer to make that same tradeoff for one mechanism in my code... > >> > "famous > >> > last words"? I hope not. I hope it just works and I don't have > >> > nightmares > >> > about lurking segfaults. :) > >> > > >> > > >> > On Thu, Apr 4, 2013 at 3:04 AM, David House <dhouse@janestreet.com> > >> > wrote: > >> >> > >> >> I don't quite understand the problem. Here's an example of how one > >> >> might use univ_map: > >> >> > >> >> open Core.Std > >> >> > >> >> module Keys : sig > >> >> val recovery : int Univ_map.Key.t > >> >> val resist_pain : float Univ_map.Key.t > >> >> end = struct > >> >> let recovery = Univ_key.Key.create "recovery" Int.sexp_of_t > >> >> let resist_pain = Univ_key.Key.create "resist_pain" Float.sexp_of_t > >> >> end > >> >> > >> >> (In practice this might be two files: keys.ml with the > implementation > >> >> and keys.mli with the signature.) You can then add things as follows: > >> >> > >> >> let add map ~key ~data = Univ_map.add_exn map key data in > >> >> let map = > >> >> Univ_map.empty > >> >> |> add ~key:Keys.recovery ~data:4 > >> >> |> add ~key:Keys.resist_pain ~data:10. > >> >> in > >> >> ... > >> >> > >> >> On 4 April 2013 09:37, Anthony Tavener <anthony.tavener@gmail.com> > >> >> wrote: > >> >> > Thank-you for the advice and pointers, folks... > >> >> > > >> >> > Well, the common problem is still the same one I've been struggling > >> >> > with: > >> >> > "creating keys", and having to access them. > >> >> > > >> >> > I can't create keys "type-free" in a common module. As I figured... > >> >> > having > >> >> > "modifier.ml" with a bunch of Key.create will have monomorphic > types > >> >> > which > >> >> > can't be resolved since with no usage in that module to make the > type > >> >> > concrete. I had a nagging feeling I'd need a "whole-program" > >> >> > compiler... > >> >> > > >> >> > Instead I'd have to create keys in modules where they are used... > but > >> >> > then I > >> >> > might have a mess of keys like Wounds.recovery, Combat.resist_pain, > >> >> > ... > >> >> > the > >> >> > problem being that only a fraction of these keys actually make > sense > >> >> > being > >> >> > associated to a particular module, and it gets confusing to know > >> >> > which > >> >> > (of > >> >> > several candidates) I decided to stash them into. This was the > >> >> > attraction to > >> >> > polymorphic variants (which I rarely use) -- they give a > pre-ordained > >> >> > unique > >> >> > ID based on a simple name... no declaration, and no module > prefixing, > >> >> > which > >> >> > seems important to me for this case. > >> >> > > >> >> > Note that I have a "database" of tables with different types > >> >> > (implemented by > >> >> > first-class modules!), and it works great for the bulk of my > >> >> > game-state, > >> >> > but > >> >> > each table is well-populated and heavily used in consistent manner. > >> >> > These > >> >> > modifiers though... they're a bit like ad-hoc message passing, > where > >> >> > I > >> >> > can > >> >> > submit any message and anywhere else add a snippet of code to > >> >> > interpret > >> >> > it > >> >> > (not that I have any of that going on, otherwise it might hold the > >> >> > solution!). > >> >> > > >> >> > > >> >> > > >> >> > On Thu, Apr 4, 2013 at 1:38 AM, Raphaël Proust < > raphlalou@gmail.com> > >> >> > wrote: > >> >> >> > >> >> >> On Thu, Apr 4, 2013 at 1:45 AM, Anthony Tavener > >> >> >> <anthony.tavener@gmail.com> wrote: > >> >> >> > […] > >> >> >> > >> >> >> And yet-another-solution, Ocsigen's Polytable: > >> >> >> http://ocsigen.org/ocsigenserver/api/Polytables > >> >> >> > >> >> >> > >> >> >> Cheers, > >> >> >> -- > >> >> >> ______________ > >> >> >> Raphaël Proust > >> >> > > >> >> > > >> > > >> > > > > > > > -- > 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 > [-- Attachment #2: Type: text/html, Size: 18259 bytes --] ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-05 19:55 ` Anthony Tavener @ 2013-04-05 20:03 ` Yaron Minsky 2013-04-05 20:27 ` Anthony Tavener 0 siblings, 1 reply; 17+ messages in thread From: Yaron Minsky @ 2013-04-05 20:03 UTC (permalink / raw) To: Anthony Tavener; +Cc: caml-list On Fri, Apr 5, 2013 at 3:55 PM, Anthony Tavener <anthony.tavener@gmail.com> wrote: > Hmm... that is some food for thought. I wasn't really considering > the creation of modules which might only hold a few keys. > Sometimes we overlook simple things. :) You've convinced me > enough to work with it and see how it fits -- it sounds like you've > had similar cases, and turning out "prettily" is appealing. > > I do have a pattern of Module.t. I remember thinking that was > an odd idiom, at first, and used an "appropriate" full name. Once > I really started using modules, and especially functors, I saw the > light. Is there a design patterns for OCaml, somewhere? :) Jason, Anil and I are working Real World OCaml. I'm hoping that will have some utility for this purpose. > Thank-you for wading through my walls of text and still offering > advice, Yaron! > > > On Fri, Apr 5, 2013 at 12:51 PM, Yaron Minsky <yminsky@janestreet.com> > wrote: >> >> On Fri, Apr 5, 2013 at 2:27 PM, Anthony Tavener >> <anthony.tavener@gmail.com> wrote: >> >> Sure, but there's nothing that requires these to be stored together. >> >> They can be scattered to the four winds, and yet all used to keep >> >> things in the same Univ_map.t. >> > >> > I was working with a universal type, but that was exactly the >> > problem I encountered: I need to create the keys and hold them >> > somewhere. My original post might have been unclear, sorry. This is >> > what I meant by having to pre-create the inj/proj pair and house >> > them somewhere... where a single file would create a bottleneck of >> > types, or scattered becomes a "mess". Scattered would prove >> > confusing I think... with hundreds of keys that have no sensible >> > "home" module. Well, some might make sense, but most of these keys >> > are interstitial -- between modules, somewhat like messages. I was >> > trying to map out where various keys would live and it wasn't >> > obvious and would continue to be unclear when I wanted to use a >> > key. There would be cognitive burden of deciding home modules for >> > things which don't fit into the modular namespace. >> >> My experience with this kind of design is that this isn't that much of >> a problem. You can create multiple modules that house keys for >> different concerns. Indeed, in such designs I've historically created >> modules with typeful interfaces that back-end on such keys, but simply >> expose a typeful way of getting access to the properties in question, >> which I think turns out quite prettily. >> >> > So, a universal type with "keys" does solve the problem, for some >> > definition of solved. My concern is that the solution is too >> > cumbersome and confusing in this use-case. Very much like arguments >> > for polymorphic variants versus variants. Unfortunately I don't see >> > any way to "embed type" in a polymorphic variant (I did check >> > whether hashes could match between different function >> > implementations of same type-signature... which was kind of silly >> > because I'm pretty sure the function address would be used in the >> > hashvalue). Although I imagine types do have hashvalues at some >> > point in the compiler! >> >> Again, If you create wrapper modules for accessing and working with >> these properties, I think everything will work smoothly. Indeed, if >> you have a module T for a given type of thing to be stored in one of >> these maps, you can build your typeful accessors for T.t into module >> T. You just need to follow the design pattern of having one module >> per type you define. >> >> > Another (failed?) possibility I mentioned as a pipe-dream: if "keys" >> > could be left open "to trust", and then verified that all trusted >> > usages lined up at link-time. After you're first reply (Yaron), I >> > thought for a moment that monomorphic types might work like that -- >> > resolved at link time. Of course, they're not, as a test quickly >> > confirmed. :( But... that's why I asked the wise body of OCamlers if >> > you have ideas -- I don't have a complete grasp of the language and >> > might be missing something or have a false assumption. It's a >> > complex type-system! I like it a lot though. >> > >> > Thinking more about this, and formulating these replies, I've >> > realized there's a common idiom in game development -- and maybe >> > software in general? It's use of string-hashes to create flexible >> > associations... from code to code, or code to data (I think that's >> > how it starts... you need to share "IDs" between code and data -- >> > and then the technique spreads). Messages, names of special points >> > in artwork (eg "primary-grip"), effects, things accessible by >> > script... But with such extensive usage, you need tools to verify >> > correct usage as much as possible. C, of course, provides few >> > guarantees of anything, so you write your own tools. OCaml is >> > tantalizing with its rich typesystem -- and polymorphic variants are >> > like built-in string-hash support. So I kept trying to figure out a >> > way to get OCaml to do the heavy lifting... for idioms I might be >> > unreasonably stuck on. ;) >> >> Maybe. My intuition is that the design pattern you're talking about >> is a good one, and really can be made to work well in OCaml. >> >> y >> >> > On Fri, Apr 5, 2013 at 10:37 AM, Yaron Minsky <yminsky@janestreet.com> >> > wrote: >> >> >> >> On Thu, Apr 4, 2013 at 2:48 PM, Anthony Tavener >> >> <anthony.tavener@gmail.com> wrote: >> >> > The problem here... >> >> > >> >> > module Keys : sig >> >> > val recovery : int Univ_map.Key.t >> >> > val resist_pain : float Univ_map.Key.t >> >> > ... >> >> > >> >> > Would be that Keys would now have dependence on types spanning the >> >> > codebase. >> >> >> >> Sure, but there's nothing that requires these to be stored together. >> >> They can be scattered to the four winds, and yet all used to keep >> >> things in the same Univ_map.t. >> >> >> >> It's honestly very much like using a dynamic language, where you get >> >> to declare new items at will that can be cast in and out from a >> >> universal type. >> >> >> >> It's probably worth reading over the Univ module in Core as well, >> >> since Univ_map is built on it, and if Univ_map doesn't quite do what >> >> you want, you can probably built what you need precisely on top of >> >> Univ. >> >> >> >> > Say these modifiers use Wounds.t, Fatigue.t, Ability.Score.t, ... >> >> > There >> >> > are >> >> > a >> >> > lot of types, which mostly have a limited scope (their own module and >> >> > a >> >> > few >> >> > others). Wouldn't it be a problem to have all types brought into this >> >> > one >> >> > module, which every other module also becomes dependent on? Maybe it >> >> > just >> >> > feels like a problem but it's just an aesthetic -- Would you do this? >> >> > A >> >> > few >> >> > hundred keys involving types from half the modules of the codebase? >> >> > >> >> > >> >> > I'm trying to use these "modifiers" for code organisation -- >> >> > declaring >> >> > snippets of functionality (all of signature 'a -> 'a... to return >> >> > modified >> >> > input) in a lightweight manner, which can be applied elsewhere. >> >> > >> >> > For example, the Virtue module can add a lot of modifers to an entity >> >> > (UID). >> >> > Say one entity has virtues of Agility, Inspirational, and Sun-cyclic >> >> > Magic, >> >> > which each adding a few modifier functions keyed to various contexts >> >> > (sun-cyclic magic: "Casting" is +3 while the sun is up). The >> >> > associated >> >> > modifiers would be picked up in code spread throughout the >> >> > application. >> >> > Rules >> >> > for combat, spellcasting, even character dialog... >> >> > >> >> > Rather than having character dialog checking for "does he have this >> >> > virtue, >> >> > or >> >> > that one? How about this ability? Spell-effects? Reputations? ..." I >> >> > want to >> >> > apply all appropriate/active modifiers for entity and situation which >> >> > come >> >> > from other rules. So, dialog code might have a current_value, then... >> >> > eg. >> >> > let modified_value = apply_modifiers_for_context entity `CharmRoll >> >> > current_value >> >> > >> >> > There is a lot of work on my project which I've been avoiding because >> >> > the >> >> > direct approach is building hairy nests of checks which call out to >> >> > code >> >> > everywhere... effectively splitting logic between the point of origin >> >> > of >> >> > a >> >> > rule, and the point of application of it. I'd rather declare the >> >> > individual >> >> > rules, each in one piece, and "magically" apply the aggregate of >> >> > rules. >> >> > >> >> > >> >> > In a way, what I'm looking for is having a grand-central lookup, >> >> > which >> >> > *trusts* that I'm using the right keys at the right time (where the >> >> > keys >> >> > are >> >> > purely symbolic with no type info)... but it would be nice if once >> >> > the >> >> > whole >> >> > program is compiled it could identify whether that trust was broken >> >> > afterall. >> >> > A bit of a pipe-dream, and maybe even flawed logic. :) >> >> > >> >> > It might sound like I'm being too picky or even whiney... that >> >> > declaring >> >> > keys >> >> > throughout the code is unacceptable, or having a file dependent on >> >> > all >> >> > types >> >> > is problematic... maybe I am? The first seems disorganised and adds a >> >> > mental burden to deciding and knowing where keys live; the second is >> >> > a >> >> > problem, isn't it? >> >> > >> >> > Dynamic languages can do what I want at the cost of typesafety. So I >> >> > might >> >> > just prefer to make that same tradeoff for one mechanism in my >> >> > code... >> >> > "famous >> >> > last words"? I hope not. I hope it just works and I don't have >> >> > nightmares >> >> > about lurking segfaults. :) >> >> > >> >> > >> >> > On Thu, Apr 4, 2013 at 3:04 AM, David House <dhouse@janestreet.com> >> >> > wrote: >> >> >> >> >> >> I don't quite understand the problem. Here's an example of how one >> >> >> might use univ_map: >> >> >> >> >> >> open Core.Std >> >> >> >> >> >> module Keys : sig >> >> >> val recovery : int Univ_map.Key.t >> >> >> val resist_pain : float Univ_map.Key.t >> >> >> end = struct >> >> >> let recovery = Univ_key.Key.create "recovery" Int.sexp_of_t >> >> >> let resist_pain = Univ_key.Key.create "resist_pain" >> >> >> Float.sexp_of_t >> >> >> end >> >> >> >> >> >> (In practice this might be two files: keys.ml with the >> >> >> implementation >> >> >> and keys.mli with the signature.) You can then add things as >> >> >> follows: >> >> >> >> >> >> let add map ~key ~data = Univ_map.add_exn map key data in >> >> >> let map = >> >> >> Univ_map.empty >> >> >> |> add ~key:Keys.recovery ~data:4 >> >> >> |> add ~key:Keys.resist_pain ~data:10. >> >> >> in >> >> >> ... >> >> >> >> >> >> On 4 April 2013 09:37, Anthony Tavener <anthony.tavener@gmail.com> >> >> >> wrote: >> >> >> > Thank-you for the advice and pointers, folks... >> >> >> > >> >> >> > Well, the common problem is still the same one I've been >> >> >> > struggling >> >> >> > with: >> >> >> > "creating keys", and having to access them. >> >> >> > >> >> >> > I can't create keys "type-free" in a common module. As I >> >> >> > figured... >> >> >> > having >> >> >> > "modifier.ml" with a bunch of Key.create will have monomorphic >> >> >> > types >> >> >> > which >> >> >> > can't be resolved since with no usage in that module to make the >> >> >> > type >> >> >> > concrete. I had a nagging feeling I'd need a "whole-program" >> >> >> > compiler... >> >> >> > >> >> >> > Instead I'd have to create keys in modules where they are used... >> >> >> > but >> >> >> > then I >> >> >> > might have a mess of keys like Wounds.recovery, >> >> >> > Combat.resist_pain, >> >> >> > ... >> >> >> > the >> >> >> > problem being that only a fraction of these keys actually make >> >> >> > sense >> >> >> > being >> >> >> > associated to a particular module, and it gets confusing to know >> >> >> > which >> >> >> > (of >> >> >> > several candidates) I decided to stash them into. This was the >> >> >> > attraction to >> >> >> > polymorphic variants (which I rarely use) -- they give a >> >> >> > pre-ordained >> >> >> > unique >> >> >> > ID based on a simple name... no declaration, and no module >> >> >> > prefixing, >> >> >> > which >> >> >> > seems important to me for this case. >> >> >> > >> >> >> > Note that I have a "database" of tables with different types >> >> >> > (implemented by >> >> >> > first-class modules!), and it works great for the bulk of my >> >> >> > game-state, >> >> >> > but >> >> >> > each table is well-populated and heavily used in consistent >> >> >> > manner. >> >> >> > These >> >> >> > modifiers though... they're a bit like ad-hoc message passing, >> >> >> > where >> >> >> > I >> >> >> > can >> >> >> > submit any message and anywhere else add a snippet of code to >> >> >> > interpret >> >> >> > it >> >> >> > (not that I have any of that going on, otherwise it might hold the >> >> >> > solution!). >> >> >> > >> >> >> > >> >> >> > >> >> >> > On Thu, Apr 4, 2013 at 1:38 AM, Raphaël Proust >> >> >> > <raphlalou@gmail.com> >> >> >> > wrote: >> >> >> >> >> >> >> >> On Thu, Apr 4, 2013 at 1:45 AM, Anthony Tavener >> >> >> >> <anthony.tavener@gmail.com> wrote: >> >> >> >> > […] >> >> >> >> >> >> >> >> And yet-another-solution, Ocsigen's Polytable: >> >> >> >> http://ocsigen.org/ocsigenserver/api/Polytables >> >> >> >> >> >> >> >> >> >> >> >> Cheers, >> >> >> >> -- >> >> >> >> ______________ >> >> >> >> Raphaël Proust >> >> >> > >> >> >> > >> >> > >> >> > >> > >> > >> >> -- >> 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 > > ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-05 20:03 ` Yaron Minsky @ 2013-04-05 20:27 ` Anthony Tavener 2013-04-08 8:33 ` David House 0 siblings, 1 reply; 17+ messages in thread From: Anthony Tavener @ 2013-04-05 20:27 UTC (permalink / raw) To: Yaron Minsky; +Cc: caml-list [-- Attachment #1: Type: text/plain, Size: 400 bytes --] On Fri, Apr 5, 2013 at 2:03 PM, Yaron Minsky <yminsky@janestreet.com> wrote: > Jason, Anil and I are working Real World OCaml. I'm hoping that will > have some utility for this purpose. Yes, I look forward to it! I have no local OCaml devs to compare with and aside from a few blogs there isn't much material. Well, there are a lot of projects with source to study, but a summary of ideas is nice. [-- Attachment #2: Type: text/html, Size: 712 bytes --] ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [Caml-list] Heterogeneous dictionary 2013-04-05 20:27 ` Anthony Tavener @ 2013-04-08 8:33 ` David House 0 siblings, 0 replies; 17+ messages in thread From: David House @ 2013-04-08 8:33 UTC (permalink / raw) To: Anthony Tavener; +Cc: Yaron Minsky, caml-list Hopefully as Jane Street releases more of our code and projects, that could be a useful set of examples. On 5 April 2013 21:27, Anthony Tavener <anthony.tavener@gmail.com> wrote: > On Fri, Apr 5, 2013 at 2:03 PM, Yaron Minsky <yminsky@janestreet.com> wrote: >> Jason, Anil and I are working Real World OCaml. I'm hoping that will >> have some utility for this purpose. > > Yes, I look forward to it! I have no local OCaml devs to compare > with and aside from a few blogs there isn't much material. Well, > there are a lot of projects with source to study, but a summary > of ideas is nice. > ^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2013-04-08 8:33 UTC | newest] Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2013-04-04 0:45 [Caml-list] Heterogeneous dictionary Anthony Tavener 2013-04-04 1:29 ` Yaron Minsky 2013-04-04 2:18 ` Anthony Tavener 2013-04-04 6:19 ` Martin Jambon 2013-04-04 7:32 ` Alain Frisch 2013-04-04 18:16 ` Martin Jambon 2013-04-04 7:38 ` Raphaël Proust 2013-04-04 8:37 ` Anthony Tavener 2013-04-04 9:04 ` David House 2013-04-04 18:48 ` Anthony Tavener 2013-04-05 16:37 ` Yaron Minsky 2013-04-05 18:27 ` Anthony Tavener 2013-04-05 18:51 ` Yaron Minsky 2013-04-05 19:55 ` Anthony Tavener 2013-04-05 20:03 ` Yaron Minsky 2013-04-05 20:27 ` Anthony Tavener 2013-04-08 8:33 ` David House
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox