Skip to content

Instantly share code, notes, and snippets.

@andrewthad
Created August 5, 2020 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewthad/d766c77d1e5a856dee85b39da28cf589 to your computer and use it in GitHub Desktop.
Save andrewthad/d766c77d1e5a856dee85b39da28cf589 to your computer and use it in GitHub Desktop.
Case on Bytes without compiler support

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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment