项目作者: subttle

项目描述 :
Finite Automata and Regular Expressions for Regular Languages in Haskell
高级语言: Haskell
项目地址: git://github.com/subttle/regular.git
创建时间: 2018-09-29T21:22:47Z
项目社区:https://github.com/subttle/regular

开源协议:BSD 3-Clause "New" or "Revised" License

下载


regular (WIP)

Type-safe formalisms for representing Regular Languages (Deterministic Finite Automata, Nondeterministic Finite Automata, Regular Expressions, etc.) in Haskell.

Example

Here is a small example of what FizzBuzz looks like with DFA:

  1. -- A DFA which accepts numbers (as a string of digits) only when
  2. -- they are evenly divisible by 5.
  3. by5 DFA Bool Fin₁₀
  4. by5 = DFA δ q f
  5. where
  6. -- Theorem: A number is divisible by 5 iff its last digit is 0 or 5.
  7. δ (Bool, Fin₁₀) Bool
  8. δ (_, 0) = True
  9. δ (_, 5) = True
  10. δ _ = False
  11. q Bool
  12. q = False
  13. f Set Bool
  14. f = singleton True
  15. -- A DFA which accepts numbers (as a string of digits) only when
  16. -- they are evenly divisible by 3.
  17. -- The transition function effectively keeps a running total modulo three by
  18. -- totaling the numeric value of its current state and the numeric value of the
  19. -- incoming digit, performing the modulo, and then converting that value back to a state.
  20. -- There is a bit of overhead complexity added by the fact that an extra state, `Left ()`,
  21. -- is introduced only to avoid accepting the empty string.
  22. by3 DFA (Either () Fin₃) Fin₁₀
  23. by3 = DFA δ (Left ()) (singleton (Right 0))
  24. where
  25. -- Theorem: A number is divisible by 3 iff the sum of its digits is divisible by 3.
  26. δ (Either () Fin₃, Fin₁₀) Either () Fin
  27. δ = Right . toEnum . (`mod` 3) . \(q, digit) fromEnum (fromRight 0 q) + fromEnum digit
  28. -- FizzBuzz using DFA
  29. main IO ()
  30. main = mapM_ (putStrLn . fizzbuzz . toDigits) [1 .. 100]
  31. where
  32. fizz [Fin₁₀] Bool
  33. fizz = accepts by3
  34. buzz [Fin₁₀] Bool
  35. buzz = accepts by5
  36. fbzz [Fin₁₀] Bool
  37. fbzz = accepts (by3 `DFA.intersection` by5)
  38. fizzbuzz [Fin₁₀] String
  39. fizzbuzz n | fbzz n = "FizzBuzz"
  40. | fizz n = "Fizz"
  41. | buzz n = "Buzz"
  42. | otherwise = n >>= show

Type Safety

Consider, for example, the GNFA q s type defined in src/GNFA.hs which is the type used to represent Generalized Nondeterministic Finite Automaton in this library.

  1. data GNFA q s where
  2. -- δ : (Q \ {qᶠ}) × (Q \ {qᵢ}) Regular Expression
  3. GNFA { delta (Either Init q, Either Final q) RegExp s } GNFA q s

Striving for correctness by construction, the type alone enforces many required invariants:

  • A GNFA must have only one transition between any two states
  • A GNFA must have no arcs coming into its start state
  • A GNFA must have no arcs leaving its final state
  • A GNFA must have only one start state and one accept state, and these cannot be the same state

That means, any time you have some GNFA (and we assume pure functional programming), those invariants hold, without any extra work or runtime checks.

Try

This project currently uses stack to build. Because the project currently depends on some older packages (algebra and containers-unicode-symbols), you may have to set the allow-newer: true option in ~/.stack/config.yaml before proceeding. Then you can try out some of the code in the REPL:

  1. git clone https://github.com/subttle/regular
  2. cd regular
  3. stack build
  4. stack ghci
  1. λ> Examples.by5'
  2. (0∣1∣2∣3∣4∣5∣6∣7∣8∣9)★·(0∣5)
  3. λ> by5' `matches` [1,2,3,4,5]
  4. True
  5. λ>

WIP status

For now it should be clear that I value correctness and simplicity over speed. Some of the code here is experimental and some is not yet structured properly, so expect major refactoring and restructuring. Once I have everything correct I can start to worry about speed. For now this code is slow.

I’m patiently (and gratefully!) waiting on a few things from some of the best projects out there right now:

  • Labelled graphs in alga
  • Linear types in Haskell
  • Better dependent type support in Haskell

I haven’t proven all class instances to be lawful yet.