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

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

  reply	other threads:[~2013-04-05 19:55 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
2013-04-05 19:55               ` Anthony Tavener [this message]
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='CAN=ouMRtB_wOvxv66xoYT2eCpmjx1oGX8831qFusg+wZmxeP9Q@mail.gmail.com' \
    --to=anthony.tavener@gmail.com \
    --cc=caml-list@inria.fr \
    --cc=yminsky@janestreet.com \
    /path/to/YOUR_REPLY

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

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