I've been reading about monads in category theory. One definition of monads uses a pair of adjoint functors. A monad is defined by a round-trip using those functors. Apparently adjunctions are very important in category theory, but I haven't seen any explanation of Haskell monads in terms of adjoint functors. Has anyone given it a thought?
Haskell – Monads as adjunctions
category-theoryfunctorhaskellmonads
Related Solutions
Here's an even simpler answer: A Monad is something that "computes" when monadic context is collapsed by join :: m (m a) -> m a
(recalling that >>=
can be defined as x >>= y = join (fmap y x)
). This is how Monads carry context through a sequential chain of computations: because at each point in the series, the context from the previous call is collapsed with the next.
A free monad satisfies all the Monad laws, but does not do any collapsing (i.e., computation). It just builds up a nested series of contexts. The user who creates such a free monadic value is responsible for doing something with those nested contexts, so that the meaning of such a composition can be deferred until after the monadic value has been created.
Your problem seems to be at least as difficult as enumerating polyominoes. There are no known fast algorithms for doing this, and the best known algorithms struggle after n=50. I doubt there is a fast way to solve this problem.
I'm not even going to pretend that this is an optimal solution but it might be useful as a reference solution. I think it at least gives the correct answer, although it takes some time. It solves the problem recursively by finding all paths of length n-1, then checking for all possible places it can add one more tile and removing duplicate solutions. It has a particularly ugly part where it checks for duplicate by converting the path to a string and comparing the strings, but it was fast to write.
Here's the output it generates:
n = 1, number of paths found = 1
n = 2, number of paths found = 4
n = 3, number of paths found = 22
n = 4, number of paths found = 113
n = 5, number of paths found = 571
n = 6, number of paths found = 2816
n = 7, number of paths found = 13616
n = 8, number of paths found = 64678
n = 9, number of paths found = 302574
And here's the code:
using System;
using System.Collections.Generic;
using System.Linq;
public struct Tile
{
public Tile(int x, int y) { X = x; Y = y; }
public readonly int X;
public readonly int Y;
public IEnumerable<Tile> GetNeighbours(int gridSize)
{
if (X > 0)
yield return new Tile(X - 1, Y);
if (X < gridSize - 1)
yield return new Tile(X + 1, Y);
if (Y > 0)
yield return new Tile(X, Y - 1);
if (Y < gridSize - 1)
yield return new Tile(X, Y + 1);
}
public override string ToString()
{
return string.Format("({0},{1})", X, Y);
}
}
public class Path
{
public Path(Tile[] tiles) { Tiles = tiles; }
public Tile[] Tiles { get; private set; }
public override string ToString()
{
return string.Join("", Tiles.Select(tile => tile.ToString()).ToArray());
}
}
public class PathFinder
{
public IEnumerable<Path> FindPaths(int n, int gridSize)
{
if (n == 1)
{
for (int x = 0; x < gridSize; ++x)
for (int y = 0; y < gridSize; ++y)
yield return new Path(new Tile[] { new Tile(x, y) });
}
else
{
Dictionary<string, object> pathsSeen = new Dictionary<string, object>();
foreach (Path shortPath in FindPaths(n - 1, gridSize))
{
foreach (Tile tile in shortPath.Tiles)
{
foreach (Tile neighbour in tile.GetNeighbours(gridSize))
{
// Ignore tiles that are already included in the path.
if (shortPath.Tiles.Contains(neighbour))
continue;
Path newPath = new Path(shortPath.Tiles
.Concat(new Tile[] { neighbour })
.OrderBy(t => t.X)
.ThenBy(t => t.Y)
.ToArray());
string pathKey = newPath.ToString();
if (!pathsSeen.ContainsKey(pathKey))
{
pathsSeen[pathKey] = null;
yield return newPath;
}
}
}
}
}
}
static void Main()
{
PathFinder pathFinder = new PathFinder();
for (int n = 1; n <= 9; ++n)
{
List<Path> paths = pathFinder.FindPaths(n, n).ToList();
Console.WriteLine("n = {0}, number of paths found = {1}", n, paths.Count);
//foreach (Path path in paths)
// Console.WriteLine(path.ToString());
}
}
}
Best Answer
Edit: Just for fun, I'm going to do this right. Original answer preserved below
The current adjunction code for category-extras now is in the adjunctions package: http://hackage.haskell.org/package/adjunctions
I'm just going to work through the state monad explicitly and simply. This code uses
Data.Functor.Compose
from the transformers package, but is otherwise self-contained.An adjunction between f (D -> C) and g (C -> D), written f -| g, can be characterized in a number of ways. We'll use the counit/unit (epsilon/eta) description, which gives two natural transformations (morphisms between functors).
Note that the "a" in counit is really the identity functor in C, and the "a" in unit is really the identity functor in D.
We can also recover the hom-set adjunction definition from the counit/unit definition.
In any case, we can now define a Monad from our unit/counit adjunction like so:
Now we can implement the classic adjunction between (a,) and (a ->):
And now a type synonym
And if we load this up in ghci, we can confirm that State is precisely our classic state monad. Note that we can take the opposite composition and get the Costate Comonad (aka the store comonad).
There are a bunch of other adjunctions we can make into monads in this fashion (such as (Bool,) Pair), but they're sort of strange monads. Unfortunately we can't do the adjunctions that induce Reader and Writer directly in Haskell in a pleasant way. We can do Cont, but as copumpkin describes, that requires an adjunction from an opposite category, so it actually uses a different "form" of the "Adjoint" typeclass that reverses some arrows. That form is also implemented in a different module in the adjunctions package.
this material is covered in a different way by Derek Elkins' article in The Monad Reader 13 -- Calculating Monads with Category Theory: http://www.haskell.org/wikiupload/8/85/TMR-Issue13.pdf
Also, Hinze's recent Kan Extensions for Program Optimization paper walks through the construction of the list monad from the adjunction between
Mon
andSet
: http://www.cs.ox.ac.uk/ralf.hinze/Kan.pdfOld answer:
Two references.
1) Category-extras delivers, as as always, with a representation of adjunctions and how monads arise from them. As usual, it's good to think with, but pretty light on documentation: http://hackage.haskell.org/packages/archive/category-extras/0.53.5/doc/html/Control-Functor-Adjunction.html
2) -Cafe also delivers with a promising but brief discussion on the role of adjunction. Some of which may help in interpreting category-extras: http://www.haskell.org/pipermail/haskell-cafe/2007-December/036328.html