Apply smiley replacement filter to Pandoc but not in code segments
There's a much shorter way to express the same functionality, but I'm not sure you'll immediately find it to be clearer.

import Data.List (intercalate)
import Data.List.Split (splitOn)
import Text.Pandoc.JSON

main :: IO ()
main = toJSONFilter smile

smile :: Inline -&gt; Inline
smile (Str s) = Str . intercalate "☺" . splitOn ":-)" $ s
smile x = x


I think the insight you missed is that the Block constructor CodeBlock contains no Inlines, so you can trust the Walkable typeclass machinery to ignore them if your smiley making code only works on Inlines. That eliminates the entire applyExceptOnCodeBlock function.

Similarly it isn't necessary to include a case for Inline's Code constructor, constructors like Code and Math contain their own raw strings and are safely ignored. Pandoc's datatypes are already structured to divide prose text from markup making it easy to define transformations by only matching on the things you do want to modify.