Secure Acquisition of Declarative Resources

I’m feeling SADR today than I was yesterday.

I spent this morning designing a generic context object atop Haskell’s Data.Typeable. The idea is to thread the context object through construction of a Sirea behavior. The context object makes it feasible to:

  1. Automatically create a thread for each logical partition (by type) without specializing Sirea’s behavior type. (Prior design had me specializing a `bcross` behavior, which is something I always hate doing.)
  2. Enable these threads to perform heavy-duty (but wait-free or at least interruptible) tasks in addition to managing RDP computations. For example, a GL thread could be responsible for displaying at 60fps in addition to RDP communication.
  3. Build simple communications mechanisms between partition threads and the RDP behavior, i.e. so RDP can supply what to display and the GL thread can be completely generic (via typeclass). The threads should communicate only via RDP, not directly with each other. Sirea would ensure snapshot consistency and a `frozen world` view between steps.

These are all features I want, but they aren’t all of the features I want.

The context object design has several weaknesses:

  1. Behaviors are difficult to compose. They can interfere via context even if they have no explicitly shared dependencies.
  2. If I use multiple contexts for independence, I cannot effectively share dynamic behaviors between them. The meaning of the behavior would change based on context.
  3. Modularity is hindered; behaviors are coupled to specific FFI modules, e.g. for OpenGL. It is difficult to transparently replace the back-end of such programs.
  4. Testing and debugging are hindered. It is difficult to use mock objects for testing, or to audit resource use to see what a program is doing.
  5. Modeling distributed programs is hindered. We need multiple instances of similar behaviors with slightly different contexts.

These problems aren’t news to me. RDP was designed with object capability model discipline, security, and design patterns in mind to solve these problems and more. I’ve even spent some time working on suitable module system designs.

But my attitude, when approaching Sirea, has been to favor the Haskell way of doing things. And Haskell’s approach to resource acquisition is about the same as C’s – ambient authority, global state, tight coupling to FFI modules, and self discipline to brave dependency hell.

I wonder if I can do better without sacrificing the dubious convenience of the familiar Haskell approach.

The most important property to me is that the meaning of a behavior depends on the context in which it is defined, not the context in which it is executed. I.e. lexical scope instead of dynamic. I could then define or acquire sub-contexts whenever I desire to control dependencies between subprograms – even create entirely `fresh` contexts to model complete isolation (up to IO). I could rely on some design patterns and self-discipline to reduce FFI coupling – i.e. embed interface objects in the context.

This separation of context means that I must supply context to define the behavior, rather than supply it after the behavior is built. Basically, it’s staged programming.

The convenience challenges then are:

  1. Developers should not be explicitly plumbing `cx` parameters through their code.
  2. It must be easy to refine contexts for different sub-programs.
  3. It should be feasible to shift gradually and semi-transparently from using hard-coded FFI to using context-based resources.

Point two could probably be met by annotating context options with plenty of metadata, i.e. so it is easier to create contexts by use of filtering and composition. Point three should fall naturally out of using a `generic` context type, i.e. I can transparently replace the code that uses GLUT directly with code that first checks the context for a GLUT interface-object and otherwise falls back on creating one with IO.

The first point is difficult. One possibility is to use GHC’s implicit parameters extension. But supplying the context while building the behavior doesn’t really appeal to me. I could try building behaviors in a State monad with context, but I think that would be even less convenient. I’d like greater separation of concerns and syntax. I’m thinking to model a static arrow transformer or similar for Sirea’s RDP model so I can supply context wherever it is needed, and manipulate sub-contexts where necessary.

If I can get this right, I will be able to compose behaviors safely and securely while still implicitly (and typefully) `declaring` the creation of threads and resources and GLUT windows to be controlled by the Sirea behavior – which is a convenience I’m not prepared to abandon.

Addendum: I’d also like to have pure dependency injection, but I have nary a clue where to start there. For now I’ll stick with the hackish mechanisms I developed for plain old CX.

This entry was posted in Language Design and tagged , , . Bookmark the permalink.

4 Responses to Secure Acquisition of Declarative Resources

  1. I’m curious why providing your context through e.g. a Reader monad would be inconvenient? I was under the impression that this is an idiomatic approach to DI in fp (eg

    • dmbarbour says:

      I could provide a stateful context through a Reader monad. But if I’m going to use state anyway, it would be better to provide a pure context through a State monad. Which is what I propose in the next article.

      If you meant a pure context, a Reader monad requires clients to build potential dependencies up front. The result is:

      • imprecise: easily build many more resources than needed.
      • entangled: ties client to implementation details of library.

      By comparison, use of State allows us to build the resources as needed and allows us providing default resources for anything the client didn’t specify. This is more precise and allows clients to control degree of entanglement by gradual, opt-in defaults.

      There are more declarative possibilities than state for DI, such as use of a stable constraint model. But I do not know how to integrate that effectively with Haskell’s IO model.

      • Dan says:

        Have you considered a reader+state monad where the reader provides capabilities (IO actions that can produce resources) and the state carries acquired resources? When a resource is required and it is not already acquired, the reader context is consulted for the correct action to take to get the resource.

      • dmbarbour says:

        That’s a good idea. The Reader would allow for some well-scoped configuration and default rules, while State would ensure nothing is built twice (or unnecessarily).

        Though, at the moment I’m aiming to achieve some all-or-nothing semantics – a sort of `resource safety` property so that I don’t fail in the middle of a build (at least, not due to missing rules). I’ve concluded that monads are a little too flexible for the analysis I’d need to perform. I might need to stick with applicative.

        Thx for the suggestion, though. If I change my mind and return to build-as-I-go semantics, I’ll return to your idea for a two-layer monad.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s