Rewrite of Sokoban using State
I think the is* functions are a bad idea - Haskell has pattern matching for this.

Also you can 'unwrap' your World when necessary and get unwrapped data from it.

data Cell = Wall | Crate | Storage | Worker | Empty

cellContents coords world = bar where
    f x = S.member coords (x ^$ world)
    bar | f wCrates = Crate
        | f wWalls = Wall
        | f wStorage = Storage
        | coords == (wWorker ^$ world) = Worker
        | otherwise = Empty


With this approach toChar can be just 

toChar :: World -> Coord -> Char

toChar2 coords world = case cellContents coords world of
      Wall      -&gt; '#'
      Worker    -&gt; '@'
      Crate     -&gt; 'o'
      Storage   -&gt; '.'
      _ -&gt; ' '


If you have already unpacked current world, you can use 

let char = toChar2 coords world


If you don't have world you can just do

char &lt;- toChar2 coord &lt;$&gt; get


The only place where you use toChar is printWorld, so it becomes simpler (no need for evalState):

let out = [[ toChar2 (x,y) w | x &lt;- [0 .. maxX]] | y &lt;- [0 .. maxY]]


setMaxCoords can be rewritten as:

setMaxCoords :: StateT World IO ()
setMaxCoords = do
    w &lt;- get
    wMax .= maxCoords w

maxCoords = foldr (app2 max) (0, 0) . allCoords where
    allCoords w = (wWorker ^$ w) : concatMap (\x -&gt; S.toList $ x ^$ w) [ wWalls, wCrates, wStorage ] where
    app2 f (x1, y1) (x2, y2) = (f x1 x2, f y1 y2)


move can be rewritten the same way as toChar2: 

move dir w = move2 (cellContents newCoord w) (cellContents newCoord' w) where
    oldCoord = wWorker ^$ w
    newCoord = moveCoord dir oldCoord
    newCoord' = moveCoord dir newCoord

    move2 Wall _ = return ()
    move2 Crate Wall = return ()
    move2 Crate _ = do
        wWorker .= newCoord
        wCrates %= delete newCoord
        wCrates %= insert newCoord'
    move2 _ _ = wWorker .= newCoord


It should be used in the loop as get &gt;&gt;= move dir.