caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Yaron Minsky <yminsky@janestreet.com>
To: Anthony Tavener <anthony.tavener@gmail.com>
Cc: "caml-list@inria.fr" <caml-list@inria.fr>
Subject: Re: [Caml-list] Heterogeneous dictionary
Date: Fri, 5 Apr 2013 14:51:23 -0400	[thread overview]
Message-ID: <CACLX4jSEm-T=QTB5Bbdkq--AEKdkTCaB2fduEJqpg=jkbiJwzg@mail.gmail.com> (raw)
In-Reply-To: <CAN=ouMS9PtAn6r58V3g-D4-ymN8UkBfFNn8F_iCF0cfX8LVvVw@mail.gmail.com>

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
>> >> >
>> >> >
>> >
>> >
>
>

  reply	other threads:[~2013-04-05 18:51 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-04-04  0:45 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 [this message]
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

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to='CACLX4jSEm-T=QTB5Bbdkq--AEKdkTCaB2fduEJqpg=jkbiJwzg@mail.gmail.com' \
    --to=yminsky@janestreet.com \
    --cc=anthony.tavener@gmail.com \
    --cc=caml-list@inria.fr \
    /path/to/YOUR_REPLY

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

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