This is an idea for how to case on a byte sequence without any special support from the compiler. The basic idea is explored in https://github.com/layer-3-communications/fortios-syslog, but there code generation is used rather than `TemplateHaskell`. The idea is that, at compile time, generating a perfect hash function for all strings of the same length is a good way to pattern match on a sequence of bytes. Roughly, we have: foo :: Bytes -> Bar foo b = case B.length b of 3 -> case hash3 b of Car | b == "car" -> ... Zoo | b == "zoo" -> ... _ -> ... 5 -> case hash5 b of Zepto | b == "zepto" -> ... Knuth | b == "knuth" -> ... Allow | b == "allow" -> ... _ -> ... _ -> ... Above, `Car`, `Zoo`, etc. are pattern synonyms for integers. Using the integers directly would be terrible and would obscure things. Even the above strategy is a bit verbose. Preferrable would be: foo :: Bytes -> Bar foo b = case b of Car -> ... Zoo -> ... Zepto -> ... ... _ -> ... With a unidirectional pattern synonym, this can be done. The check for the length, the computation of the hash, the casing on the hash, and the check against the literal all get rolled in there together. I believe that GHC might optimize this to do things in the same order as above (we definitely want the check against the literal to happen last), but I need to confirm that. In the second strategy, the patterns need to be generated, and `TemplateHaskell` would be a good way to generate them. Perhaps something like: makeBytesPatterns ["car","zoo","zepto",...] Probably sitting in its own module, could generate patterns like: pattern Car :: Bytes pattern Car <- ((\x -> B.length x == 3 && hash3 x == 26938246 && equals3 x 'c' 'a' 'r') -> True)