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 wrote: > On Fri, Apr 5, 2013 at 2:27 PM, Anthony Tavener > 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 > > wrote: > >> > >> On Thu, Apr 4, 2013 at 2:48 PM, Anthony Tavener > >> 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 > >> > 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 > >> >> 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 > >> >> >> 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 >