Let us consider a dwarf wandering in a tunnel. I will define a type that represents this situation thusly:
data X a = X { xs :: [a], i :: Int }
display :: X Bool -> IO ()
display X{..} = putStrLn (concatMap f xs) where { f True = "*" ; f False = "-" }
Here you see a dwarf in a section of a tunnel:
λ display x
-*---
It is discovered that a pointed container is an instance of Comonad
. I can use this
instance here to define a function that simulates my dwarf moving right:
shiftRight :: X Bool -> Bool
shiftRight x@X{..} | let i' = i - 1 in i' `isInRange` x && xs !! i' = True
| otherwise = False
See:
λ traverse_ display $ scanl (&) x (replicate 4 (extend shiftRight))
-*---
--*--
---*-
----*
-----
Spectacularly, this same operation works with any number of dwarves, in any pointed container, and so can be extended to a whole dwarf fortress if desired. I can similarly define a function that moves a dwarf leftwards, or in any other deterministic fashion.
But now what if I want my dwarf to wander around aimlessly? Now my "shift randomly" must only place a dwarf to the right if the same dwarf is not being placed to the left (for that would make two dwarves out of one), and also it must never place two dwarves in the same place (which would make one dwarf out of two). In other words, "shift randomly" must be linear (as in "linear logic") when applied over a comonadic fortress.
One approach I have in mind is to assign some sort of state to dwarves that tracks the available
moves for a dwarf, removing moves from every relevant dwarf when we decide that the location is
taken by one of them. This way, the remaining dwarves will not be able to take that move. Or we
may track availability of locations. I am thinking that some sort of a "monadic" extendM
might be useful. (It would compare to the usual extend
as traverse
compares to fmap
.)
But I am not aware of any prior art.