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 16:03:22 -0400	[thread overview]
Message-ID: <CACLX4jQwHCbAjF5YmgvZ16n6B-=jJQ1ciQ-w5mb=8mnKSnHaig@mail.gmail.com> (raw)
In-Reply-To: <CAN=ouMRtB_wOvxv66xoYT2eCpmjx1oGX8831qFusg+wZmxeP9Q@mail.gmail.com>

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

  reply	other threads:[~2013-04-05 20:03 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
2013-04-05 20:03                 ` Yaron Minsky [this message]
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='CACLX4jQwHCbAjF5YmgvZ16n6B-=jJQ1ciQ-w5mb=8mnKSnHaig@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).