开发者

Removing String double-quotes in Haskell

This function generates simple .dot files for visualizing automata transition functions using Graphviz. It's primary purpose is debugging large sets of automatically generated transitions (e.g., the inflections of Latin verbs).

prepGraph :: ( ... ) => NFA c b a -> [String]
prepGraph nfa = "digraph finite_state_machine {"
              : wrapSp "rankdir = LR"
              : wrapSp ("node [shape = circle]" ++ (mapSp (states nfa \\ terminal nfa)))
              : wrapSp ("node [shape = doublecircle]" ++ (mapSp $ terminal nfa))
              : formatGraph nfa ++ ["}"]

formatGraph :: ( ... ) => NFA c b a -> [String]
formatGraph = map formatDelta . deltaTuples
 where formatDelta (a, a', bc) = wrapSp (mkArrow a a' ++ " " ++ mkLabel bc)
       mkArrow x y   = show x ++ " -> " ++ show y
       mkLabel (y, z) = case z of
         (Just t) -> "[ label = \"(" ++ show y ++ ", " ++ show t ++ ")\" ]"
         Nothing  -> "[ label = \"(" ++ show y ++ ", " ++ "Null" ++ ")\" ]"

where wrap, wrapSp and mapSp are formatting function开发者_StackOverflows, as is deltaTuples.

The problem is that formatGraph retains double quotes around Strings, which causes errors in Graphviz. E.g., when I print unlines $ prepGraph to a file, I get things like:

0 -> 1 [ label = "('a', "N. SF")" ];

instead of

0 -> 1 [ label = "('a', N. SF)" ];

(However, "Null" seems to work fine, and outputs perfectly well). Now of course the string "N. SF" isn't the actual form I use to store inflections, but that form does include a String or two. So how can I tell Haskell: when you show a String values, don't double-quote it?


Check out how Martin Erwig handled the same problem in Data.Graph.Inductive.Graphviz:

http://hackage.haskell.org/packages/archive/fgl/5.4.2.3/doc/html/src/Data-Graph-Inductive-Graphviz.html

The function you're looking for is "sq" at the bottom:

sq :: String -> String
sq s@[c]                     = s
sq ('"':s)  | last s == '"'  = init s
            | otherwise      = s
sq ('\'':s) | last s == '\'' = init s
            | otherwise      = s
sq s                         = s

(check out the context and adapt for your own code, of course)


Use dotgen package - it has special safeguards in place to prevent forbidden chars from sneaking into attribute values.


You could define your own typeClass like this:

class GShow a where
   gShow :: a -> String
   gShow = show

instance GShow String where
   show = id

instance GShow Integer
instance GShow Char
-- And so on for all the types you need.

The default implementation for "gShow" is "show", so you don't need a "where" clause for every instance. But you do need all the instances, which is a bit of a drag.

Alternatively you could use overlapping instances. I think (although I haven't tried it) that this will let you replace the list of instances using the default "gShow" by a single line:

instance (Show a) => GShow a

The idea is that with overlapping instances the compiler will chose the most specific instance available. So for strings it will pick the string instance over the more general one, and for everything else the general one is the only one that matches.


It seems a little ugly, but you could apply a filter to show t

filter (/='"') (show t)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜