The H2 Wiki


Bluefin is a capability system

– Tom Ellis, September 2026

When I released the first version of my Haskell package Bluefin in early 2024 I called it an “effect system”, specifically, an “IO-wrapper” (or “analytic”) effect system1, in which regard it follows effectful. Now in late 2026 I have decided to start referring to Bluefin as a “capability system” first and an “effect system” second, that is, something like “a capability system that can be used to obtain the benefits of a traditional Haskell effect system”.

Capability systems

A capability system is a programming language framework for working with “capabilities”, a capability being a program entity that references a program or system resource and, moreover, grants authority to access that resource.

In a capability system, each capability points from a subject to a resource. Consequently every capability can serve both to designate which resource to access, and to provide the authority to perform that access.2

An example of a capability that most programmers will have encountered is the file handle. In the words of Lampson3:

A familiar example in operating systems is a file descriptor or file handle for an open file. When a process opens the file, the OS [after checking that the process has sufficient rights to access the file] creates a handle for the open file.

The notion of capability was originally introduced for security purposes, as a model for access control and protection in operating systems4. se4L is a capability-based kernel with an executable specification written in Haskell! Capabilities are also used in programming languages (e.g. Pony), where they are often referred to as “object capabilities”.

After writing Bluefin I realised that there’s no reason to view capabilities in a narrow, security-only context. Effect systems can be considered to be capability systems. Read on to learn more.

Effect systems

Definition

In Haskell the notion of “effect system” is not precisely defined. However, all usages of the terminology have something important in common: an effect system allows the programmer to circumscribe the range of externally visible behaviours of program components.

Interpreted narrowly, “effect system” might mean a library in the style of extensible-effects5, in which all effects take place in a specific monad (e.g. in effectfulEff”) and the possibility of an effect is indicated by a constraint, i.e. at the type level. For example, the following function (using types from effectful) may only interact with a database and/or throw an exception of String:

operationEffectful ::
  (Database :> es, Error String :> es) =>
  Eff es ()

A slight broadening of this narrow interpretation permits Bluefin to be described as an effect system. In Bluefin, the difference is that the possibility of an effect is indicated by a value-level argument, for example:

operationBluefin ::
  Database es ->
  Throw String es ->
  Eff es ()

Further stretching the definition allows MTL to be considered an effect system:

operationMTL ::
  (MonadDatabase m, MonadError String m) =>
  m ()

or even transformers:

operationTransformers ::
  DatabaseT (Except String) ()

A very broad definition allows ST to be considered an effect system (that only allows mutable state effects) and even IO (which allows mutable state, exceptions, and the whole kitchen sink of interactions with external resources).

(The order in which the effect systems were presented here is close to the opposite of the order in which they were developed historically. For more information see my talk “A History of Effect Systems”.)

Absence of effects

It is important to emphasise that the reason effect systems are useful is not that they allow the programmer to define program components that perform effects. Rather, they are useful because they allow the programmer to define program components that are forbidden from performing effects outside a specified set.

In the extreme we can forbid all externally visible effects, thereby using the type system to enforce that our operation must be what is sometimes called a “pure function” or a “mathematical function”. The types for an operation that takes a String and returns an Int, performing no externally visible effects, might look as follows in different effect systems:

computeEffectful :: String -> Eff es Int
computeBluefin :: String -> Eff es Int
computeMTL :: Monad m => String -> m Int
computeTransformers :: String -> Identity Int

In ST and IO there is no way of circumscribing the externally visible effects short of not using them at all, so we could settle for the following (which has no effect system in sight):

computeFunction :: String -> Int

Capability systems versus effect systems

In a capability system there are operations you can only perform if you have access to a capability that provides authority to perform them. Above we saw an example: you can only write to an open file if you its handle in scope. Correspondingly, in an effect system there are effectful operations you can only perform if their corresponding effect is somehow “in scope”. For example, you can only throw an exception if some sort of error, exception or throw effect is in scope (in extensible-effects and descendants and MTL, at the type level; in Bluefin at the value level; in transformers provided by a monad transformer). What, then, is the difference between capability systems and effect systems? I think there is no difference!

Let’s investigate the supposed correspondence a bit more closely. Can all capabilities be treated as effects? When introducing capability systems above we noted that authority to access a file is provided by a capability, often called a “file descriptor” or “file handle”. Likewise, the authority to access a database would be considered a capability, generally provided through a “connection handle”. The operation* examples above show how different Haskell effect systems represent such a capability. Haskell effect systems already have the ability to pass around, implicitly or explicitly, a program resource that is required to use a particular program component.

How about the converse? Can all effects be treated as capabilities? I think yes. In order to perform a particular effectful operation something has to be “in scope”. It can be interpreted as the capability to perform that effect.

Effects as capabilities

Let’s look at a few examples of effects as capabilities. Firstly, a state effect can be interpreted as a capability that grants authority to interact with a mutable value of a given type. I think it is particularly interesting to look at the case of ST, IO and Bluefin, where STRef, IORef and Modify respectively provide what is traditionally called a “mutable reference”, at the value level. A “mutable reference” matches exactly the definition of capability we saw earlier (“a program entity that references a program or system resource and, moreover, grants authority to access that resource”). A mutable reference is a pointer which references some mutable region of memory and “grants access to it”, i.e. if you hold such a mutable reference you can use it to modify the mutable region of memory.

Secondly, an exception effect can be interpreted as the capability which grants the authority to throw a value of a particular type to a particular handler. Again, I find the case of Bluefin particularly interesting: a value of type Throw grants access to the exception mechanism, allowing you to throw an exception to the handler that was installed by try (or catch or handle), the same site that introduced the Throw into scope6.

Thirdly, a reader effect can be interpreted as the capability to interact with some thread-local, locally-mutable state. No such primitive mutable state reference exists in Haskell at present, but there is a proposal to add one. I like the name IOScopedRef for the new reference type7. (Bluefin and effectful simulate this kind of reference, as Ask and Reader respectively.)

Finally, “I/O effects”, i.e. the ability to interact in arbitrary ways with Haskell’s runtime system (RTS), can be interpreted as the capability granting authority to do so. That is to say, effectful’s type level IOE, Bluefin’s value level IOE, MTL’s MonadIO constraint and the plain IO monad itself can all be interpreted as capabilities granting authority to interact with the RTS.

“Bluefin is a capability system”

So why have I decided to describe Bluefin as a “capability system”? Because I think that “capability” terminology communicates better than “effect” terminology. Firstly, the latter raises the awkward question of what an “effect” is. One might attempt to define “effect” in terms of opposition to a notion of “pure”, but I’ve never seen a convincing such attempt. In fact I haven’t come across a convincing definition of “pure” either8.

Secondly, I think that, regrettably, the terminology “effect system” gives the wrong impression about Haskell to outsiders, specifically that, unlike other languages, Haskell needs a “special system” to do “effects”. The converse is true: other languages, unlike Haskell, cannot circumscribe the possible range of effects. I hope that using the terminology “capability system” to describe Bluefin moves the perception of Haskell by outsiders a modest amount in a favourable direction.

Thirdly, “effect systems” are used as a software engineering tool for structuring programs in the large, but the name doesn’t fit the use case. A talk by Tom Wells entitled “Encoding Architecture Into Your Code (So Agents Can’t Break It)” describes how to use an effect system to enforce layering in an application by defining abstraction boundaries. The executive summary is that effect systems can be used to constrain architecture. That’s a concise way of summarising a benefit of Haskell I’ve found when programming in the large. But the phrase sounds odd to me. What does architecture have to do with effects? “Capability systems can be used to constrain architecture” sounds much more natural! In fact it’s so natural it’s almost obvious how that works: you give each component in your architecture the capability to perform only what it needs to. (A very interesting open question remains: how do you use your capability system to propagate capabilities to where they are needed?)

Agent-generated code

Tom Wells’s talk was specifically on the topic of maintaining good architecture when code is generated by AI agents. In a world where the cost of generating technically correct code is declining rapidly but good design is not becoming commensurately easier I believe that effect systems have a critical role to play. Their role is not limited to architecture either: they can help enforce security properties of code at scale. I think the Haskell community will have an easier time explaining how our favourite language helps in that regard, and maybe win a few converts to our community, if instead of calling them “effect systems” we call them “capability systems”.

References


  1. For more information on “IO-wrapper” or “analytic” effect systems, as well as the history of effect systems in Haskell in general, see my talk A History of Effect Systems↩︎

  2. Capability Myths Demolished↩︎

  3. Practical Principles for Computer Security↩︎

  4. Programming semantics for multiprogrammed computations↩︎

  5. extensible-effects originates from Extensible Effects: an alternative to Monad Transformers by Kiselyov, Sabry and Swords↩︎

  6. The ealiest example of this idea of “scoped exception” may have been Brachthäuser, Schuster, Ostermann in Effects as Capabilities: Effect Handlers and Lightweight Effect Polymorphism.↩︎

  7. See also my article Haskell’s missing mutable reference type↩︎

  8. Unless it’s just “‘pure’ means ‘referentially transparent’”↩︎