Algebras for the Masses!

According to Wikipedia, “an Algebraic Structure is a set with one or more finitary operations defined on it that satisfies a list of axioms”. From a programming perspective, that sounds like a bunch of methods defined on a type. In fact, we can find many of those algebras represented as type classes in libraries such as scalaz or cats. This way of representing algebras is pretty related to object algebras. However, it’s quite common to hear about F-algebras as well, an abstraction that arises from the field of Category Theory. Today, we’ll see not only that both representations are isomorphic, but also how to systematically generate conversions between them. To validate those transformations, we’ll scratch the surface of Matryoshka to fold several expressions with the aforementioned algebra representations. So here we go!

Algebras and Their Representations

Undoubtedly, one of the most widespread algebraic structures in the functional programming community is monoid. Despite its simplicity, it turns out to be very powerful. Typically, in Scala type class libraries, monoid is represented as follows:

trait OMonoid[A] {
  def mzero(): A
  def mappend(a1: A, a2: A): A
}

This type class is what is known as the object algebra interface, an interface of an abstract factory to create expressions. It contains two methods: mzero and mappend which correspond with the two operations that describe this particular algebra. Once we have created the algebra interface, we could provide instances for it, that are also known as object algebras. A common monoid instance is sum:

val sumOMonoid: OMonoid[Int] = new OMonoid[Int] {
  def mzero(): Int = 0
  def mappend(a1: Int, a2: Int): Int = a1 + a2
}

Once we have shown object algebra fundamentals, it’s time to focus on F-algebras. This is a really simple abstraction that consists of a Functor F[_], a carrier type A and an algebra structure (the function itself):

type FAlgebra[F[_], A] = F[A] => A

At first glance, this looks very different from the object algebra approach for monoids. However, as we will see, the translation is completely natural. Indeed, this representation just packs all the algebra operations into a unique function. Thereby, the major challenge here is to identify the corresponding functor for monoids, which is an Algebraic Data Type with a representative for every operation conforming the algebra. We refer to it as the algebra signature:

sealed trait Σ[A]
case class MZero[A]() extends Σ[A]
case class MAppend[A](a1: A, a2: A) extends Σ[A]

Once the functor is defined, we can modularize Monoid as an F-algebra:

type FMonoid[A] = FAlgebra[Σ, A]

Finally, we could provide a sum instance for the brand new monoid representation, as we did with the previous approach:

val sumFMonoid: FMonoid[Int] = {
  case Mzero() => 0
  case Mappend(a1, a2) => a1 + a2
}

We claim that sumFMonoid is isomorphic to sumOMonoid. In order to provide such an evidence, we show the isomorphism between OMonoid and FMonoid:

val monoidIso = new (OMonoid <~> FMonoid) {

  val to = new (OMonoid ~> FMonoid) {
    def apply[A](omonoid: OMonoid[A]) = {
      case Mzero() => omonoid.mzero
      case Mappend(a1, a2) => omonoid.mappend(a1, a2)
    }
  }

  val from = new (FMonoid ~> Monoid) {
    def apply[A](fmonoid: FAlgebra[Σ, A]) = new FMonoid[A] {
      def mzero = fmonoid(Mzero())
      def mappend(a1: A, a2: A) = fmonoid(Mappend(a1, a2))
    }
  }
}

(*) Notice that we have ignored the monoid laws along the article for simplicity, but keep in mind that they constitute a fundamental part of every algebra.

Given this situation, the question we should be asking is: “What is the best algebra representation for us?” Sadly, there’s no clear answer to this. On the one hand, there is F-algebra. Undoubtedly, this representation is more modular. In fact, it is used in projects such as Matryoshka, a library of recursion-schemes that is able to generate fixed points for any Functor, or define a generic catamorphism (or fold) once and for all, which works for any F-algebra. On the other hand, there is the object algebra representation, closer to the widespread programming interfaces, that we can find in libraries such as scalaz or cats. Although not as modular as F-algebras, this representation is powerful enough to interpret algebra expressions with little effort. See this paper on shallow embedding to get a better intuition on that. Therefore, both representations do appear in prominent libraries of the functional programming community. Wouldn’t it be nice to have them coexisting?

Macro @algebra to Provide Conversions

As we have seen in the previous section, turning object algebras into F-algebras (and viceversa) is straightforward. Besides, we noticed that both algebra representations are used in everyday programming. For all these reasons, we decided to code an experimental macro (@algebra) to make both representations live together. The macro annotation can be applied to an object algebra Interface:

@algebra trait OMonoid[A] {
  def mzero(): A
  def mappend(a1: A, a2: A): A
}

This annotation removes boilerplate by automatically generating some F-algebra encodings. They should enable us to translate object algebras wherever an F-algebra is required. To check that behaviour, we’re going to invoke a Matryoshka catamorphism (cata) that requires an F-algebra as input parameter, but we’ll be implementing object algebras instead. Besides, we’ll be using the tiny language (num literals and multiplications) that is used in Matryoshka’s introduction, so we recommend the reader to glance at it before moving ahead. Then, you should be able to appreciate that Expr leads to the following object algebra interface:

@algebra trait ExprAlg[A] {
  def num(value: Long): A
  def mul(l: A, r: A): A
}

First of all, our macro has to generate the corresponding Expr signature. So, our auto generated companion for ExprAlg will contain:

sealed abstract class Σ[_]
case class Num[A](value: Long) extends Σ[A]
case class Mul[A](l: A, r: A) extends Σ[A]

In this section from Matryoshka’s introduction, we see that it’s required to provide a functor for Expr prior to apply a cata. Our macro is able to derive the Functor instance for Σ, so we don’t have to worry about that. The document shows also an eval F-algebra, that we can translate easily to an object algebra:

implicit def eval = new Expr[Long] {
  def num(value: Long) = value
  def mul(l: Long, r: Long) = l * r
}

Notice that we marked it as implicit, because it will be necessary for the next task, which is invoking the catamorphism over an expression. Firstly, we need to declare the expression to be folded, I mean, evaluated. We can copy someExpr as is, and it will compile smoothly, since the Mul and Num case classes are generated by the macro as well:

def someExpr[T](implicit T: Corecursive.Aux[T, Σ]): T =
  Mul(Num[T](2).embed, Mul(Num[T](3).embed,
    Num[T](4).embed).embed).embed

Finally, we can invoke the cata. As we noted previously, it requires an F-algebra as input. Thereby, we use the FAlgebra summoner, generated by the macro, that detects the implicit eval and turns it into a compatible F-algebra to feed the function.

someExpr[Mu[Σ]].cata(FAlgebra[Long]) // ⇒ 24

To sum up, we applied our macro annotation to ExprAlg to generate some utilities to deal with F-algebras. Then, we defined our eval as an object algebra. As the generated encodings knew how to turn it into a F-algebra, we could invoke Matryoshka’s cata with this algebra safely. Thus, we reach our objective of making both representations coexist nicely.

Future Work

Today, we have seen OMonoid[A] and ExprAlg[A] as algebra examples, both demanding a concrete type parameter. However, there are algebras that are parametrized by a type constructor. Take Monad[F[_]] as an example. In this particular situation, we can’t generate isomorphisms with F-algebras as we know them. Instead, we have to deal with F-algebras for Higher Order Functors. Our macro @algebra is able to detect GADTs and generate the corresponding encodings. This is still very experimental, but you can find an example here.

By now, we have placed @algebra in azucar (spanish word for “sugar”), a library where we plan to deploy more utilities to deal with (co)algebras. If you have some feedback or suggestion to improve it, we’d be very glad to hear from you. Anyway, we hope you’ve enjoyed reading!

Posted in Uncategorized | Leave a comment

From “Hello, world!” to “Hello, monad!” (Part II/III)

In the first part of this series, we set forth the essence of functional programming, namely, being declarative. This was illustrated with the ubiquitous “Hello, world!” example, a ridiculously simple program which, nonetheless, allowed us to introduce the major concepts involved in purely functional programming: declarative functions, languages, and interpreters. It’s time now to show that this approach actually scales up for larger and more complex programs.

In the following paragraphs, you’ll find a series of impure IO programs that represent purification challenges. Compare to the simple “Hello, world!” program, these new programs feature additional IO instructions, and an increasingly complex control flow structure. These extra levels of complexity will force us to enrich the simple IOProgram DSL and interpreter that we created to purify the simple “Hello, world!” program. For easy of reference, we reproduce that initial purification bellow:

object HelloWorld{
  /* Impure program */
  def helloWorld: String =
    println("Hello, world!")

  /* Functional solution */
  object Fun {
    // Language
    type IOProgram = Print
    case class Print(msg: String)

    // Pure function
    def pureHello(): IOProgram =
      Print("Hello, world!")

    // Interpreter
    def run(program: IOProgram): Unit =
      program match {
        case Print(msg) => println(msg)
      }

    // Impure program (modularised)
    def hello() = run(pureHello())
  }
}

Say what?

Our first challenge consists in purifying the following impure program:

def sayWhat: String =
  readLine

Similarly to the “Hello, world!” program, the “sayWhat” program consists of a single IO instruction. In this case, when the program is run it will immediately block until we type something in the console. Then, it will return the string typed, rather than Unit:

scala> SayWhat.sayWhat
(type "something")
res0: String = "something"

In order to purify any program we have to return pure values that represent a description of the logic we want to accomplish. In order to describe this logic, we use the IOProgram DSL, but the current version of this DSL does only offer a Write instruction, so we have to extend it with a new Read command:

type IOProgram[A] = IOEffect[A]

sealed trait IOEffect[A]
case class Write(msg: String) extends IOEffect[Unit]
case object Read extends IOEffect[String]

There are several things going on here:

  • We chose to use an ADT (Algebraic Data Type) to represent our instructions. We created an effect Language to perform IO operations, composed by two instructions, one to read from and the other to write to the console. And our programs consist of either one of these instructions.
  • The new Read instruction is a case object because it is a singleton instance; there can only exist one and only one instance of Read as it has no arguments.
  • Another thing to point out is that we parameterized our IOEffect and IOProgram ADTs. We did that because we need to store the return type of our instructions somewhere in order to be able to implement the interpreter. So in this case we use a phantom type to carry that information over. Thus, the IOEffect algebraic data type is what is known as a Generalised Algebraic Data type (GADT).

We got it. Now we can express the impure “sayWhat” program in a pure fashion as follows:

def pureSayWhat: IOProgram[String] = Read

As simple as that. But things become more complicated when we try to update our interpreter:

def run(program: IOProgram): ??? = ...

Now our programs are parameterized so we need to change the signature a little bit; remember that the return type is stored in the program type parameter. We can use good old pattern matching to know which instruction we are dealing with (Scala’s support for pattern matching GADTs suffices in this case):

def run[A](program: IOProgram[A]): A =
  program match {
    case Write(msg) => println(msg)
    case Read => readLine
  }

The only thing left to do is reimplementing in a modular fashion our equivalent impure function. I’ll leave the complete example below:

object SayWhat {
  /* Impure program */
  def sayWhat: String = readLine

  /* Functional solution */
  object Fun {
    // Language
    type IOProgram[A] = IOEffect[A]

    sealed trait IOEffect[A]
    case class Write(msg: String) extends IOEffect[Unit]
    case object Read extends IOEffect[String]

    // Pure Program
    def pureSayWhat: IOProgram[String] = Read

    // Interpreter
    def run[A](program: IOProgram[A]): A =
      program match {
        case Write(msg) => println(msg)
        case Read => readLine
      }

    // Composition
    def sayWhat: String = run(pureSayWhat)
  }
}

Say What? (reloaded)

We’ll start now building programs with more than one instruction. In this case we are going to print something to the console and then read the user’s input.

Impure program

def helloSayWhat: String = {
  println("Hello, say something:")
  readLine
}

As you can see, this is a common imperative program which can be read out aloud as follows: “first, do this; next, do this”.

Pure function and language

So far, our program definition has been just a type alias of a single instruction, but now we want our programs to be able to represent two-instructions programs as well. It turns out our program definition must be also an ADT:

sealed trait IOProgram[A]
case class Single[A](e: IOEffect[A]) extends IOProgram[A]
case class Sequence[A, B](e1: IOProgram[A], e2: IOProgram[B]) extends IOProgram[B]

def pureHelloSayWhat: IOProgram[String] =
  Sequence(
    Single(Write("Hello, say something:")),
    Single(Read))

As you can see, our programs can now be made up of just a Single instruction or a Sequence of two programs.

Interpreter

We must now change the interpreter accordingly. In particular, we need two interpreters, one for programs and one for effects:

def run[A](program: IOProgram[A]): A =
  program match {
    case Single(e) => runEffect(e)
    case Sequence(p1, p2) =>
      runProgram(p1) ; runProgram(p2)
  }

def runEffect[A](effect: IOEffect[A]): A =
  effect match {
    case Write(msg) => println(msg)
    case Read => readLine
  }

Composition

The only thing left is to rewrite the impure program in a modular fashion:

def sayWhat: String = run(pureHelloSayWhat)

Echo, echo!

In our next program we’ll complicate the control flow a little bit.

Impure program

def echo: Unit = {
  val read: String = readLine
  println(read)
}

Note that the println instruction is writing the result of the read operation. This is a behaviour we can’t describe with our program representation yet. The problem is that the Sequence case doesn’t allow us to use the result of the first program. We thus need somehow to represent context-dependent programs, i.e. programs that depend on the results of previous ones. Let’s fix that.

Pure function and language

sealed trait IOProgram[A]
case class Single[A](e: IOEffect[A]) extends IOProgram[A]
case class Sequence[A, B](e1: IOProgram[A],
  e2: A => IOProgram[B]) extends IOProgram[B]

def pureEcho: IOProgram[Unit] =
  Sequence(
    Single(Read), read =>
    Single(Write(read)) )

As simple as that, the new version of Sequence carries as its second parameter a program that is allowed to depend on a value of type A, i.e. the type of values returned when the first program is interpreted. Of course, the intention is that the interpreter will apply this function to that precise value, as will be shown in the next section. By the way, does the signature of the new version of Sequence ring a bell?

Interpreter

As commented previously, the new version of the interpreter will simply need to modify the way in which sequenced programs are executed:

def runProgram[A](program: IOProgram[A]): A =
  program match {
    case Single(e) => runEffect(e)
    case Sequence(p, next) =>
      val res = runProgram(p)
      runProgram(next(res))
  }

Composition

The last thing to do is to reimplement the impure function in a modular way by applying the interpreter to the result of the pure function.

def echo: Unit = runProgram(pureEcho)

On pure values

There are still some impure IO programs we can’t represent with our current IOProgram ADT. In particular, think of imperative programs structured as follows: “Do this program; then, do this other program, possible taking into account the result of the last program; etc.; finally, return this value, possible taking into account the results of the last steps.”. It’s the last step which can’t be represented. For instance, let’s consider the following program.

Impure program

def echo(): String = {
  val read: String = readLine
  println(read)
  read
}

This program is similar to the last one, but this time we return the String read, i.e., a pure value. So let’s add this new functionality to our ADT.

Pure function and language

sealed trait IOProgram[A]
case class Single[A](e: IOEffect[A]) extends IOProgram[A]
case class Sequence[A, B](e1: IOProgram[A],
  e2: A => IOProgram[B]) extends IOProgram[B]
case class Value[A](a: A) extends IOProgram[A]

def pureEcho: IOProgram[String] =
  Sequence(
    Single(Read), read =>
      Sequence(
        Write(read), _ =>
          Value(read)))

This is the final form of our ADT, whereby a program can be one of three:

  • Single: A single instruction.
  • Sequence: A sequence of context-dependent programs.
  • Value: A pure value (e.g. a String, Int, MyFancyClass, etc.)

Interpreter

In order to update the interpreter, we just have to deal with our new type of IO programs.

def runProgram[A](program: IOProgram[A]): A =
  program match {
    case Single(e) => runEffect(e)
    case Sequence(p, next) =>
      val res = runProgram(p)
      runProgram(next(res))
    case Value(a) => a
  }

As you can see, this interpretation is fairly easy: a program Value(a) just means “returns a”, which is what our interpreter does.

Composition

Last, we compose interpreter and pure function as usual to obtain a modular version of the original impure program:

def echo: String = runProgram(pureEcho)

Conclusion

This post aimed at showing that no matter how complex you impure programs are, you can always design a DSL to represent those programs in a purely declarative way. In our case, the DSL for building IO programs we ended up with is pretty expressive. In fact, we can represent any kind of imperative control flow with it. Try it!

There are, however, two major flaws we have still to deal with. First, we have to admit that the readability of programs written in the final IOProgram DSL is … poor, to say the least. Second, there is a lot of boilerplate involved in the design of the IOProgram type. Indeed, no matter the type of DSL we are dealing with (based on IO instructions, File system operations, Web service calls, etc.), if we need imperative features, we will need to copy & paste the same Sequence and Value cases. We leave the solution to these problems for the next and last post of this series!

Edit: All code from this post can be found here.

Posted in Embedded DSLs, functional programming, Scala | Leave a comment

Lens, State Is Your Father

In our last post, we introduced IOCoalgebras as an alternative way of representing coalgebras from an algebraic viewpoint, where Lens was used as a guiding example. In fact, lens is an abstraction that belongs to the group of Optics, a great source of fascinating machines. We encourage you to watch this nice introduction to optics because we’ll show more optic examples under the IOCoalgebra perspective. While doing so, we’ll find out that this new representation let us identify and clarify some connections between optics and State. Finally, those connections will be analyzed in a real-world setting, specifically, in the state module from Monocle. Let us not waste time, there is plenty of work to do!

(*) All the encodings associated to this post have been collected here, where the same sectioning structure is followed.

Optics as Coalgebras

First of all, let’s recall the IOCoalgebra type constructor:

type IOCoalgebra[IOAlg[_[_]], Step[_, _], S] = IOAlg[Step[S, ?]]

As you can see, it receives three type arguments: the object algebra interface, the state-based action or step, and the state type. Once provided, coalgebras are defined as a state-based interpretation of the specified algebra. Take Lens as an example of IOCoalgebra:

trait LensAlg[A, P[_]] {
  def get: P[A]
  def set(a: A): P[Unit]
}

type IOLens[S, A] = IOCoalgebra[LensAlg[A, ?[_]], State, S]

(*) This is a simple Lens, in opposition to a polymorphic one. We will only consider simple optics for the rest of the article.

If we expand IOLens[S, A], we get LensAlg[A, State[S, ?]], which is a perfectly valid representation for lenses as demonstrated by the following isomorphism:

def lensIso[S, A] = new (Lens[S, A] <=> IOLens[S, A]) {

  def from: IOLens[S, A] => Lens[S, A] =
    ioln => Lens[S, A](ioln.get.eval)(a => ioln.set(a).exec)

  def to: Lens[S, A] => IOLens[S, A] = ln => new IOLens[S, A] {
    def get: State[S, A] = State.gets(ln.get)
    def set(a: A): State[S, Unit] = State.modify(ln.set(a))
  }
}

We’ll see more details about lenses in later sections but, for now let’s keep diving through other optics, starting with Optional:

trait OptionalAlg[A, P[_]] {
  def getOption: P[Option[A]]
  def set(a: A): P[Unit]
}

type IOOptional[S, A] = IOCoalgebra[OptionalAlg[A, ?[_]], State, S]

This optic just replaces IOLens’ get with getOption, stating that it’s not always possible to return the inner value, and thence the resulting Option[A]. As far as we are concerned, there aren’t more significant changes, given that State is used as step as well. Thereby, we can move on to Setters:

trait SetterAlg[A, P[_]] {
  def modify(f: A => A): P[Unit]
}

type IOSetter[S, A] = IOCoalgebra[SetterAlg[A, ?[_]], State, S]

In fact, this is a kind of relaxed lens that has lost the ability to “get” the focus, but is still able to update it. Notice that set can be automatically derived in terms of modify. Again, State is perfectly fine to model the step associated to this optic. Finally, there is Getter:

trait GetterAlg[A, P[_]] {
  def get: P[A]
}

type IOGetter[S, A] = IOCoalgebra[GetterAlg[A, ?[_]], Reader, S]

This new optic is pretty much like a lens where the set method has been taken off, and it only remains get. Although we could use State to represent the state-based action, we’ll take another path here. Since there isn’t a real state in the background that we need to thread, ie. we can only “get” the inner value, Reader could be used as step instead. As an additional observation, realize that LensAlg could have been implemented as a combination of GetterAlg and SetterAlg.

There are still more optics in the wild, such as Fold and Traversal, but we’re currently working on their corresponding IOCoalgebra representation. However, the ones that have already been shown are good enough to establish some relations between optics and the state monad.

Optics and State Connections

Dealing with lenses and dealing with state feels like doing very similar things. In both settings there is a state that could be queried and updated. However, if we want to go deeper with this connection, we need to compare apples to apples. So, what’s the algebra for State? Indeed, this algebra is very well known, it’s named MonadState:

trait MonadState[F[_], S] extends Monad[F] {
  def get: F[S]
  def put(s: S): F[Unit]

  def gets[A](f: S => A): F[A] = 
    map(get)(f)

  def modify(f: S => S): F[Unit] = 
    bind(get)(f andThen put)
}

This MonadState version is a simplification of what we may find in a library such as scalaz or cats. The algebra is parametrized with two types: the state-based action F and the state S itself. If we look inside the typeclass, we find two abstract methods: get to obtain the current state and put to overwrite it, given a new one passed as argument. Those abstract methods, in combination with the fact that MonadState inherits Monad, let us implement gets and modify as derived methods. This sounds familiar, doesn’t it? It’s just the lens algebra along with the program examples that we used in our last post! Putting it all together:

trait LensAlg[A, P[_]] {
  def get: P[A]
  def set(a: A): P[Unit]

  def gets[B](
      f: A => B)(implicit
      F: Functor[P]): P[B] =
    get map f

  def modify(
      f: A => A)(implicit
      M: Monad[P]): P[Unit] =
    get >>= (f andThen set)
}

(*) Notice that we could have had LensAlg extending Monad as well, but this decoupling seems nicer to us, since each program requires only the exact level of power to proceed. For instance, Functor is powerful enough to implement gets, so no Monad evidence is needed.

Apparently, the only difference among LensAlg and MonadState lies in the way we use the additional type parameter. On the one hand, LensAlg has a type parameter A, which we understand as the focus or inner state contextualized within an outer state. On the other hand, we tend to think of MonadState‘s S parameter as the unique global state where focus is put. Thereby, types instantiating this typeclass usually make reference to that type parameter, as one could appreciate in the State instance for MonadState. However, we could avoid that common practice and use a different type as companion. In fact, by applying this idea in the previous instance, we get a new lens representation:

type MSLens[S, A] = MonadState[State[S, ?], A]

(*) The isomorphism between IOLens and MSLens is almost trivial, given the similarities among their algebras. Indeed, you can check it here.

Lastly, we can’t forget about one of the most essential elements conforming an algebra: its laws. MonadState laws are fairly known in the functional programming community. However, the laws associated to our LensAlg aren’t clear. Luckily, we don’t have to start this work from scratch, since lens laws are a good starting point. Despite the similarity between both packages of laws (look at their names!) we have still to formalize this connection. Probably, this task will shed even more light on this section.

Monocle and State

Connections between optics and state have already been identified. Proof of this can be found in Monocle, the most popular Scala optic library nowadays, which includes a state module containing facilities to combine some optics with State. What follows is a simplification (removes polymorphic stuff) of the class that provides conversions from lens actions to state ones:

class StateLensOps[S, A](lens: Lens[S, A]) {
  def toState: State[S, A] = ...
  def mod(f: A => A): State[S, A] = ...
  def assign(a: A): State[S, A] = ...
  ...
}

For instance, mod is a shortcut for applying lens.modify over the standing outer state and returning the resulting inner value. The next snippet, extracted from Monocle (type annotations were added for clarity), shows this method in action:

case class Person(name: String, age: Int)
val _age: Lens[Person, Int] = GenLens[Person](_.age)
val p: Person = Person("John", 30)

test("mod") {
  val increment: State[Person, Int] = _age mod (_ + 1)

  increment.run(p) shouldEqual ((Person("John", 31), 31))
}

That said, how can we harness from our optic representation to analyze this module? Well, first of all, it would be nice to carry out the same exercise from the IOLens perspective:

case class Person(name: String, age: Int)
val _ioage: IOLens[Person, Int] =
  IOLens(_.age)(age => _.copy(age = age))
val p: Person = Person("John", 30)

test("mod") {
  val increment: State[Person, Int] = 
    (_ioage modify (_ + 1)) >> (_ioage get)

  increment.run(p) shouldEqual ((Person("John", 31), 31))
}

Leaving aside the different types returned by _age mod (_ + 1) and _ioage modify (_ + 1), we could say that both instructions are pretty much the same. However, mod is an action located in an external state module while modify is just a primitive belonging to IOLens. Is this a mere coincidence? To answer this question, we have formalized this kind of connections in a table:

Monocle State-Lens Action IOLens Action Return Type
toState get State[S, A]
? set(a: A) State[S, Unit]
? gets(f: A ⇒ B) State[S, B]
? modify(f: A ⇒ A) State[S, Unit]
mod(f: A ⇒ A) ? State[S, A]
modo(f: A ⇒ A) ? State[S, A]
assign(a: A) ? State[S, A]
assigno(a: A) ? State[S, A]

What this table tells us is how the actions correspond to each other. For instance, the first raw shows that toState (from Monocle) corresponds directly with get (from our IOLens), both generating a program whose type is State[S, A]. The second raw contains a new element ?, which informs us that there’s no corresponding action for set in Monocle. Given the multitude of gaps in the table, we could determine that we’re dealing with such different stuff, but if you squint your eyes, it’s not hard to appreciate that mod(o) and assign(o) are very close to modify and set, respectively. In fact, as we saw while defining increment, mod is just a combination of get and modify. So, it seems to exist a strong connection between the IOLens primitives and the actions that could be placed in the state module for lenses. The obvious question to be asked now is: Is there such a connection between the state module and other optics? In fact, Monocle also provides facilities to combine State and Optionals, so we can create the same table for it:

Monocle State-Optional Action IOOptional Action Return Type
toState getOption State[S, Option[A]]
? set(a: A) State[S, Unit]
? gets(f: A ⇒ B) State[S, Option[B]]
? modify(f: A ⇒ A) State[S, Unit]
modo(f: A ⇒ A) ? State[S, Option[A]]
assigno(a: A) ? State[S, Option[A]]

Again, the results are very similar to the ones we extracted from IOLens. In fact, we claim that any IOCoalgebra-based optic which can be interpreted into State may contain a representative in the state module, and the actions that the module may include for each of them are just its associated primitives and derived methods. But, what about Getters, where both State and Reader are suitable instances? Well, the State part is clear, we can add a new representative for Getter in the state module. However, the interesting insight comes with Reader: identifying new interpretations means identifying new modules. In this sense, we could consider including a new module reader in the library. Obviously, we could fulfill that module by following the same ideas that we showed for state.

To sum up, by following this approach, we have obtained a framework to systematically determine:

  • The appropriateness of including a new module.
  • The optics that it may support.
  • The methods it may contain for every optic.

This is a nice help, isn’t it?

Discussion and Ongoing Work

Today, we have seen that IOCoalgebras served us two purposes, both of them involving understandability. First of all, we have identified an unexpected connection between Lens and the State Monad. In fact, we have defined Lens in terms of MonadState, so we had to explain Lens who was his biological father, and that was tough for her! Secondly, we have described a systematic process to create and fulfill Monocle’s peripheral modules, such as state. In this sense, if we go one step further, we could think of those peripheral modules as particular interpretations of our optic algebras. This perspective makes the aforementioned process entirely dispensable, since optic instances would replace the module itself. As a result, logic wouldn’t end up being contaminated with new names such as assign or mod, when all they really mean is set and modify, respectively.

As we mentioned before, we still have to translate other optics into their corresponding IOCoalgebra representation and identify the laws associated to the algebras. Besides, we focused on simple optics, but we should contemplate the polymorphic nature of optics to analyze its implications in the global picture. Anyway, optics are just a source of very low-level machines that conform one of the first steps in the pursue of our general objective, which is programming larger machines, ie. reactive systems, by combining smaller ones. It’s precisely within this context where our optics, in combination with many other machines from here and there, should shine. In this sense, there’s still a lot of work to do, but at least we could see that isolating algebras from state concerns has turned out to be a nice design pattern.

Posted in algebra, coalgebra, Lens, Optics, State, Type Class | Leave a comment

Yo Dawg, We Put an Algebra in Your Coalgebra

As Dan Piponi suggested in Cofree Meets Free, we may think of coalgebraic things as machines with buttons. In this post, we take this metaphor seriously and show how we can use algebras to model the Input/Output interface of the machine, i.e. its buttons. Prior to that, we’ll make a brief introduction on coalgebras as they are usually shown, namely as F-coalgebras.

What are F-coalgebras?

F-coalgebra (or functor-coalgebra) is just a reversed version of the more popular concept of F-algebra, both of them belonging to the mystical world of Category Theory. The most widespread representation of an F-algebra is

type Algebra[F[_], X] = F[X] => X

(using Scala here) Paraphrasing Bartosz Milewski, “It always amazes me how much you can do with so little”. I believe that its dual counterpart

type Coalgebra[F[_], X] = X => F[X]

deserves the very same amazingness, so today we’ll put focus on them.

Given the previous representation, we notice that F-coalgebras are composed of a carrier X, a functor F[_] and a structure X => F[X] itself. What can we do with such a thing? Since we are just software developer muggles (vs matemagicians), we need familiar abstractions to deal with coalgebras. Therefore, we like to think of them as machines with buttons, which know how to forward a particular state (maybe requiring some input) to the next one (maybe attaching some output along) by pressing the aforementioned buttons. Now, let’s find out some examples of mainstream machines that we, as functional programmers, already know:

// Generator Machine (Streams)
type GeneratorF[A, S] = (A, S)
type Generator[A, S]  = Coalgebra[GeneratorF[A, ?], S]

// Mealy Automata Machine
type AutomataF[I, S] = I => (Boolean, S)
type Automata[I, S]  = Coalgebra[AutomataF[I, ?], S]

// Lens Machine
type LensF[A, S] = (A, A => S)
type Lens[A, S]  = Coalgebra[LensF[A, ?], S]

Firstly, let’s expand Generator[A, S] into S => (A, S) which is something easier to deal with. Indeed, it’s just a function that, given an initial state S, it returns both the head A and the tail S associated to that original state. It’s the simplest specification of a generator machine that one could find! Given a concrete specification and once provided an initial state, we could build a standard Stream of As.

Secondly, we showed a Mealy Automata. Again, let’s turn Automata[I, S] into S => I => (Boolean, S) to see it clearer: given the current state S and any input I we can determine both the finality Boolean condition and the new state S.

Finally, we saw Lens. Notice that the type parameters are reversed if we compare this lens with the “official” representation (eg. lens, Monocle, etc.). This is just to provide homogeneity with the rest of machines, where the state S is kept as the last parameter. As usual, let’s expand Lens[A, S] to obtain S => (A, A => S). This tell us that given an initial state S, we could either get the smaller piece A or set the whole state with a brand new A.

So far, we have seen the typical representation for some prominent coalgebras. On the other hand, we claimed that we like to think of those coalgebras as machines with buttons that let us make them work. That machine abstraction seems nice, but I agree it’s difficult to see those buttons right now. So, let’s find them!

Coalgebras as machines? Then, show me the buttons!

As promised, we’ll dive into F-coalgebras to find some buttons. I anticipate that those buttons are kind of special, since they could require some input in order to be pressed and they could return some output after that action. We’re going to use Lens as a guiding example but we’ll show the final derivation for our three machines at the end as well. So, we start from this representation:

type Lens[A, S] = S => (A, (A => S))

If we apply basic math, we can split this representation into a tuple, getting an isomorphic one:

type Lens[A, S] = (S => A, S => A => S)

Trust me when I say that every element in this tuple corresponds with an input-output button, but we still have to make them uniform. First of all, we’re going to flip the function at the second position, so the input for that button stays in the left hand side:

type Lens[A, S] = (S => A, A => S => S)

Our button at the first position has no input, but we can create an artificial one to make the input slot uniform:

type Lens[A, S] = (Unit => S => A, A => S => S)

Once provided the input for the buttons, we reach different situations. On the first button there is S => A which is a kind of observation where the state remains as is. However, in the second button, there is S => S which is clearly a state transformation with no output attached to it. If we return the original state along with the observed output in the first button and provide an artificial output for the second one, we get our uniform buttons, both with an input, an output and the resulting state.

type Lens[A, S] = (Unit => S => (S, A), A => S => (S, Unit))

If we squint a bit, we can find an old good friend hidden in the right hand side of our buttons, the State monad, leading us to a new representation where both tuple elements are Kleisli arrows:

type Lens[A, S] = (Unit => State[S, A], A => State[S, Unit])

Finally, we can achieve a final step, aiming at both naming the buttons and being closer to an object-oriented mindset:

trait Lens[A, S] {
  def get(): State[S, A]
  def set(a: A): State[S, Unit]
}

So here we are! We have turned an F-coalgebra into a trait that represents a machine where buttons (get & set) are certainly determined. Obviously, pressing a button is synonym for invoking a method belonging to that machine. The returning value represents the state transformation that we must apply over the current state to make it advance. If we apply the same derivation to streams and automata we get similar representations:

trait Generator[A, S] {
  def head(): State[S, A]
  def tail(): State[S, Unit]
}

trait Automata[I, S] {
  def next(i: I): State[S, Boolean]
}

We’re glad we found our buttons, so we can reinforce the machine intuition, but stranger things have happened along the way… The coalgebraic Upside Down world is not quite far from the algebraic one.

Buttons are Algebras

In the previous section we made a derivation from the Lens F-coalgebra to a trait Lens where buttons are made explicit. However, that representation was mixing state and input-output concerns. If we go a step further, we can decouple both aspects by abstracting the state away from the specification, to obtain:

trait LensAlg[A, P[_]] {
  def get(): P[A]
  def set(a: A): P[Unit]
}

type Lens[A, S] = LensAlg[A, State[S, ?]]

So, lenses can be understood as a state-based interpretation of a particular Input/Output algebra. We can distinguish in this kind of specification between two components: the IO interface and the state transition component. Why would we want to define our lenses, or any other coalgebra, in this way? One advantage is that, once we get this representation, where input-output buttons are completely isolated, we can make machine programs that are completely decoupled from the state component, and just depend on the input-output interface. Take modify, a standard lens method, as an example:

def modify[A, P[_]](
    f: A => A)(implicit
    P: LensAlg[A, P],
    M: Monad[P]): P[Unit] =
  P.get >>= (P.set compose f)

Notice that although modify constrains P to be monadic, this restriction could be different in other scenarios, as we can see with gets, where Functor is powerful enough to fulfil the programmer needs:

def gets[A, B, P[_]](
    f: A => B)(implicit
    P: LensAlg[A, P],
    F: Functor[P]): P[B] =
  P.get map f

These programs are absolutely declarative since nothing has been said about P[_] yet, except for the fundamental constraints. Indeed, this way of programming should be pretty familiar for a functional programmer: the step that abstracted the state away led us to a (Higher Kinded) object-algebra interface, which is just an alternative way of representing algebras (as F-algebras are).

Ongoing Work

We started this post talking about F-coalgebras, type Coalgebra[F[_], X] = X => F[X], and then we turned our lens coalgebra example into a new representation where buttons and state transformation concerns are clearly identified (rather than being hidden into the functor ‘F’). Indeed, we may tentatively put forward IO-coalgebras as a particular class of coalgebras, and define lenses as follows:

type IOCoalgebra[IOAlg[_[_]], Step[_, _], S] = IOAlg[Step[S, ?]]
type Lens[A, S] = IOCoalgebra[LensAlg[A, ?], State, S]

As we said in the previous section, this representation empowers us to use the existing algebraic knowledge to deal with coalgebras. So, although we started our journey aiming at the specification of machines, we were brought back to the algebraic world! So, which is the connection between both worlds? In principle, what we suggest is that coalgebras might be viewed as state-based interpretations of algebras. Now, whether any F-Coalgebra can be represented as an IO-Coalgebra is something that has to be shown. And, additionally, we should also identify the constraints in the IOCoalgebra definition that allows us to prove that the resulting formula is actually a coalgebra.

On future posts, we’ll be talking about cofree coalgebras as universal machines. As we will see, those cofree machines exploit the button intuition to simulate any other machine in different contexts. By now, we’d be really grateful to receive any kind of feedback to discuss the proposed connection between languages and machines. Hope you enjoyed reading!

Posted in algebra, coalgebra, Embedded DSLs, machine, Scala, Type Class | 2 Comments

From “Hello, world!” to “Hello, monad!” (Part I)

This is the first instalment of a series of posts about the essence of functional programming. The only purpose of this series is to illustrate the defining features of this style of programming using different examples of increasing complexity. We will start with the ubiquitous “Hello, world!” and will eventually arrive at … (throat clearing) monads. But we won’t argue that monads are the essence of functional programming, and, ultimately, do not intend these posts to be about monads. In fact, we will stumble upon monads without actually looking for them, much in the same spirit of Dan Piponi’s “You Could Have Invented Monads! (And Maybe You Already Have.)“.

There is a major difference between Dan’s post and these ones, however: we won’t be using Haskell but Scala, a language which unlike Haskell is not purely functional, i.e. that allows us to write non-functional programs. But this feature won’t be a drawback at all. On the contrary, we think that it will allow us to emphasise some aspects (e.g. the interpreters) that may go unnoticed using a language like Haskell. Let’s start with our first example!

Hello, functional world!

This is a possible way of writing the “Hello, world!” program in Scala:

object Example1{
  def hello(): Unit =
    println("Hello, world!")
}

which can be run in the Scala REPL as follows:

scala> Example1.hello()
Hello, world!

As you can see, when this function is run the message “Hello, world!” is printed in the console. This is called a side effect, and it was indeed the purpose of the program. But this also means that our implementation is not purely functional. Why? Because functional programming is all about writing pure functions: functions which receive some input, compute some values and do nothing else. In a strongly-typed language such as Scala, we can witness the non-functional character of some function as follows: if the function does nothing else than returning values of the type declared in its signature, then it’s a pure function; otherwise, it’s an impure function: its signature declares that it does one thing, but it also does something more behind the compiler’s back. We may thus also say that impure functions work in the black market, beyond the reach of the compiler’s type system (our best ally!).

But, if pure functions only allow us to return values, how can we then print something to the console? How can we then execute any kind of effect (read something from the database, invoke a web service, write to a file, etc.)? How can we then do something useful at all? The answer is that you can’t do those things with pure functions alone. Functional programming rests upon a basic modularity principle which tells us to decompose our applications into two kinds of modules: (1) functional modules, made up of pure functions that are responsible for computing what has to be done, and (2) non-functional modules made up of impure functions or programs in charge of actually doing it. It’s this latter kind of modules which will do the dirty job of actually interacting with the outside world and executing the desired effects; and it’s the only responsibility of the functional modules to determine which effects have to be executed. When programming functionally, you should not forget this fact: impure functions will eventually be necessary. The only thing that functional programming mandates is to segregate impure functions, so that the ivory tower where pure functions live is as large as possible, and the kitchen where impure, side-effecting functions operate is reduced to the bare minimum (which, nonetheless, in many applications might be quite large).

This limitation on the things that functional programming can do is a self-imposed constraint that doesn’t actually constrain its range of application domains. Indeed, we can apply the functional programming style to any kind of application domain you may think of. So, what about our impure “Hello, world!” program? Can we purify it? Sure we can. But then, how can we disentangle its pure and impure parts? Essentially, functional programming tells us to proceed as follows (line numbers refer to the code snippet bellow):

  • First, we define a data type that allows us to describe the kinds of effects we are dealing withIn our “Hello, world!” example, we want to talk about printing strings somewhere, so we will define the Print data type (cf. line 8).
  • Second, we implement a function which computes the particular desired effects in terms of an instance of the previous data type. Our program then will be pretty simple (line 12): it simply returns the value Print("Hello, world!"). Note that this function is pure!
  • Third, we implement a function that receives an instance of the effect data type, and executes it in any way we want. In our example, we will implement a function that receives a Print value and executes a println instruction to write the desired message to the console (line 17). This function is thus impure!
  • Last, we compose our program out of the previous two functions, obtaining, of course, an impure program (line 21).

The resulting program is implemented in the Fun module (pun intended):

object Example1{ 

  /* Functional purification */
  object Fun{

    // Language
    type IOProgram = Print
    case class Print(msg: String)

    // Program
    def pureHello(): IOProgram =
      Print("Hello, world!")

    // Interpreter
    def run(program: IOProgram): Unit =
      program match {
        case Print(msg) => println(msg)
      }

    // Composition
    def hello() = run(pureHello())
  }
}

The equivalent program to our initial impure solution Example1.hello is the (also impure) function Example1.Fun.hello. Both functions has the same functionality, i.e. they both do the same thing, and from the point of view of the functional requirements of our tiny application, they are both correct. However, they are far from being similar in terms of their reusability, composability, testability, and other non-functional requirements. We won’t explain in this post why the functional solution offers better non-functional guarantees, but ultimately the reason lies behind its better modularisation: whereas the Example1.hello function is monolithic, its functional counterpart is made up of two parts: the pureHello function and the impure function run.

Now, an important remark concerning the above code: note that we defined the alias IOProgram for our effect data type, and that we used the labels LanguageProgram and Interpreter for the different parts of our functional solution. This is not accidental, and it points at the interpreter pattern, arguably the essence of functional programming:

  • First, our effect data type can be regarded as the language we use to describe the desired effects of our application. As part of this language, we can have different types of single effects, such as the Print effect or instruction. Also, since languages are used to write programs, expressions in our effect language can be called programs, and we can use the word “program” to name the type of the overall effect language. In our case, since we are dealing with IO effects, IOProgram is a good name. Last, note that the purpose of our language is very specific: we want to be able to write programs that just build upon IO instructions, so IOProgram is actually a domain-specific language (DSL). Here there are some hand-crafted programs of our IO DSL:
    scala> import Example1.Fun._
    import Example1.Fun._
    scala> val program1: IOProgram = Print("hi!")
    program1: Example1.Fun.IOProgram = Print(hi!)
    scala> val program2: IOProgram = Print("dummy program!")
    program2: Example1.Fun.IOProgram = Print(dummy program!)
    
  • So, pure functions return programs: this means that functional programming is intimately related to metaprogramming! And when we say that functional programming is declarative, we mean that functional programs just declare or describe what has to be done in terms of expressions or values. To convince yourself that our pureHello function is really declarative (i.e. pure), just execute it on the REPL. You will see that the only thing that happens during its execution is that a new value is computed by the runtime system (note that the output that you’ll see in the REPL is not a result of the pureHello execution, but of the REPL itself):
    scala> Example1.Fun.pureHello()
    res1: Example1.Fun.IOProgram = Print(Hello, world!)
    
  • Once we execute a pure function and obtain a program, what is left is to actually run that program. But our program is an expression, pure syntax, so we have to choose a particular interpretation before we can actually run it. In our case, we chose to interpret our IOPrograms in term of console instructions, but we are free to interpret it otherwise (file IO, socket, etc.). When you run the program computed by our pureHello function, you will actually see the intended side effects:
    scala> Example1.Fun.run(Example1.Fun.pureHello())
    Hello, world!
    

So, this is basically the structure of functional programming-based applications: domain-specific languages, pure functions that return programs in those DSLs, and interpreters that execute those programs. Note that these interpreters may be implemented in such a way that our programs are not directly executed but are instead translated to programs of some lower-level intermediate language (in a pure way). Eventually, however, we will reach the “bare metal” and we will be able to observe some side effect in the real world.

The IO programs that we are able to express with our current definition of the type IOProgram are very simple. In fact, we can just create programs that write single messages. Accordingly, the range of impure functions that we can purify is pretty limited. In our next posts, we’ll challenge our IO DSL with more complex and realistic scenarios, and will see how it has to be extended in order to cope with them.

Edit: All code from this post can be found here.

Posted in functional programming, Scala | Tagged , | 2 Comments

The Speech Console

During these months, we have tried to explain Speech using different strategies and metaphors, with varying results. For instance, we have defined Speech as

“A process-oriented programming language”

or as

“A DSL for programming the business logic of social applications”

These definitions are precise and correct, but not very effective. In fact, the common reaction from the audience to these definitions is “WAT” 😉

Juan: In this post, I will try to shed some light on these definitions with the help of … THE SPEECH CONSOLE!

You: wat


Well, let’s try it.

What is the Speech Console?

Think of chat clients, such as IRC clients, instant messengers, etc. These tools allow people to communicate about any topic, almost in real-time. The Speech console can be thought of a chat client, in the sense that its purpose is essentially to allow users to communicate and engage in conversation. And, consequently, you can similarly think of the speech virtual machine as a kind of chat server which enables and mediates these communications. In fact, if we launch the Speech server with the minimum configuration possible, what we obtain is something similar to a chat server. The following program, which you can find in the Getting started section of the speechlang.org site, precisely do that:

object Main extends App{
  object Test extends org.hablapps.speech.web.PlainSystem
  Test.launch
}

Running this program will make the Speech server available at the localhost:8111 default address. Then, you can point the browser to the URL localhost:8111/console to get access to the Speech console. The following video illustrates this process and a sample session with two users.

 

The Speech Server as a structured communication infrastructure

But the Speech Virtual Machine is much more than a simple chat server, of course. Even with its minimal configuration, the Speech server allows users to structure their communications around a hierarchy of interaction contexts (akin to chat rooms), and their activity in terms of a hierarchy of roles played within those interactions. Thus, besides saying arbitrary things, the previous video showed how users can set up new interaction contexts, join and assign other users to them, say things within those contexts, and, eventually, leave and close the interactions.

The set upclose, join, leave, etc., message types are standard declarations provided by Speech that allow users to modify the interaction space. Within the Speech console, you can get help on these commands by typing “help say”. But, besides saying things, users can also see what is happening (e.g. which interactions are taking place? which roles do I play?, etc.). Typing “help see” will give you explanations on how to observe the particular way in which interactions are structured within the Speech server at a given moment.

Speech as a language for programming a communication infrastructure

But the major difference between the Speech server and a simple communication infrastructure is not its ability to hold structured conversations, but the fact that it can be programmed. To understand in which sense the Speech server can be programmed, note that the previous minimum configuration represents a state of anarchy: people can say what they want, and structure their conversations the way they like; moreover, there are no rules: someone may set up a new interaction, and this interaction could be closed immediately by any other one.

Now, think of the way people communicate in a given context. First, the shape of interactions and roles that people play, as well as the types of things they say, are commonly constrained to certain types. And the things that they can say, see and do depend on the kind of role they play, as well as on the specific circumstances in which they attempt to do it. So, people’s interactions are commonly shaped and ruled … at least to some extent, and a Speech program precisely encodes this rules so that they can be interpreted at runtime by the Speech virtual machine.

Simulating Twitter interaction through the Speech console

For instance, which are the constraints imposed by twitter on user interaction? which norms are enforced? Basically, interactions within the Twitter community are shaped around member accounts and lists; interacting users can only be guests or registered tweeters, who can be followers of other tweeters and be listed; concerning the things they say, guests can only set up new accounts, whereas tweeters can only tweet messages whose lengths are constrained to 140 chars, re-tweet others’ messages, join other tweeter accounts as followers (i.e. follow other users), etc. Concerning norms, tweeters can only follow other users if they are not blocked by them; tweets issued within some account are automatically notified to their followers; etc. These and other norms and rules are part of the specification of Twitter as a communication network, and these norms and types of interactions, roles and message types, can easily be programmed in Speech.

Let’s suppose that the org.hablapps.twitter.Program  trait implements these types and norms; then, you can tell the Speech server that it must manage user interaction according to the structure and rules of twitter, with a simple mixin composition:

import org.hablapps.{ speech, twitter }
object TwitterWeb extends App {
  object System extends speech.web.PlainSystem with twitter.Program {
  System.launch
}

Once you execute this program, you can launch the Speech console and test different twitter scenarios. The following video shows the following one:

  • First, a new twitter community is created (named “habla”)
  • Then, a guest user enters the community and decides to register himself as a Twitter user (@serrano)
  • Another twitter account is created for tweeter @morena; this time the account is private
  • @morena follows @serrano, so that she receives whatever serrano tweets within his account
  • @serrano attempts to follow @morena, but her account is private, so the “following” declaration is kept pending for approval
  • @morena allows @serrano to follow her
  • Eventually, @morena decides to unfollow @serrano and she also fires his follower role within her account

In sum …

In the light of all this, what can we say about the purpose and proposition value of Speech? First, the Speech console shows that Speech is only suitable for programming software which aim at managing user interaction. More specifically, Speech allows programmers to implement the structure and rules that govern the interactions of users within the application domain, i.e. the joint activities (or processes) carried out by people within a given social context. Thus, we can think of Speech as a (social) process-oriented programming language. Moreover, Speech is not a general purpose language but a domain specific language: in fact, Speech is a language that allows us to program a structured communication infrastructure, not a general purpose machine. Last, since the business logic of social applications largely deal with the kind of interaction requirements addressed by Speech, it can be described as a DSL for programming the business logic of social applications. 

Why should we program social applications in Speech? First, because programmers don’t have to start from scratch but from a programmable communication infrastructure (i.e. less code needed); and, second, because the Scala embedding of Speech allows us to implement the structure and rules of interactions in a very concise and understandable way. We strive to achieving minimum levels of accidental complexity, so that functional requirements can be implemented in the most direct way possible. In next posts, we will tell you about new improvements in the Speech embedding.

Have a great holidays!

Posted in Apps, Speech, Web console | Leave a comment

Sample Speech Apps: Twitter, Trac, Do&Follow … and Big Brothapp!

Which kinds of applications are most suitable for Speech? How do we design a Speech app? How do we implement a Speech design using its Scala embedding? To answer these questions in a pleasant and entertaining way, we completely re-designed our web page and chose four applications in quite different domains for illustrating the virtues of our DSL. The whole Habla Computing team has been involved in the design and implementation of these apps. Here they are!

We all know Twitter. It’s a micro-blogging platform which allows people to publish short-message updates. Can we program the structure of Twitter interactions and its communication rules in Speech? Of course! Accounts, tweeters, tweets, statistics, and so forth, can be very easily mapped into social interactions, agent roles, speech acts and information resources  – the different kinds of social abstractions put forward by Speech. “Following” rules for private accounts or blocking users can also be expressed very easily using empowerment and permission rules. Monitoring rules can also represent communication rules (e.g. forwarding tweets to your followers) and notification preferences (e.g. being informed that you’ve got a new follower) straightforwardly.

Do&follow up is a task-based management application which allows project responsibles to assign tasks to people, monitor progress, attach information resources to each ongoing task, launch automatically new tasks according to a pre-established workflow, etc. It belongs to a completely different domain from Twitter … isn’t it? Not really. Indeed, both workflow management software and social networks ultimately deal with people and their needs for communication and collaboration. The differences between them simply lie in the way communications are structured and governed. Thus, we can model tasks and their responsible people as particular types of social interactions and agent roles, respectively, and represent workflows through life-cycle initiation and finalisation rules.

The Trac application is an issue tracking system for software development projects. It’s somewhat similar to the do&follow up application, but you won’t find a pre-established workflow here. The trac application is interesting from the point of view of Speech because it allows us to illustrate its ability to support both structured and unstructured communication modalities: on the one hand, issues go through a number of well-defined stages and follow specific rules for assignments and completion; on the other, the owner, reporter and arbitrary members of the application can engage in free conversation concerning an issue. Again, it’s all about about speech acts with different normative weights and constraints.

The Big Brothapp prototype was designed after the rules of the big brother reality game show. The purpose of the application is to assist the producers in the management of the TV show, particularly by enabling a computational representation of the house, the eviction processes and any other aspect which belongs to the “rules fo the game”. These rules can be accommodated in a very convenient way by Speech, so that that the structure and dynamics of the contest can be formalised with empowerment and life-cycle rules, standard speech acts, etc., very easily!

Watch the design and coding video guides!

We used Big Brothapp to illustrate the Speech design and coding processes through a series of videos that you can find in our web page. The apps section also shows videos for the other applications, as well as their source code on github. If you have any comments, questions or wonder how you preferred app could be modeled in Speech, please tell us!

Last, you will also find in the apps section direct links to runtime deployments of these apps through their respective Consoles – but this will be the subject for the next post. Enjoy!

Posted in Apps, Speech | Leave a comment