snake wrangling snake wrangling
play

SNAKE WRANGLING SNAKE WRANGLING Isaac Elliott How can we bring the - PowerPoint PPT Presentation

SNAKE WRANGLING SNAKE WRANGLING Isaac Elliott How can we bring the benefits of better languages to existing codebases? Language tooling Language tooling for Python append_to :: Statement append_to = def_ "append_to" [ p_


  1. SNAKE WRANGLING SNAKE WRANGLING Isaac Elliott

  2. How can we bring the benefits of better languages to existing codebases?

  3. Language tooling

  4. Language tooling for Python

  5. append_to :: Statement append_to = def_ "append_to" [ p_ "element", k_ "to" (list_ []) ] [ expr_ $ call_ ("to" /> "append") [ "element" ] , return_ "to" ]

  6. def append_to(element, to=[]): to.append(element) return to

  7. def append_to(element, to=None): if to is None: to = [] to.append(element) return to

  8. fixMutableDefaultArguments :: Statement -> Maybe Statement

  9. rewriteOn _Statements fixMutableDefaultArguments :: Module -> Module

  10. DESIGN DESIGN

  11. Parse & Print

  12. (print . parse) :: String -> String = id

  13. Write & Check

  14. Optics

  15. PROPERTY TESTING PROPERTY TESTING

  16. Property of plus : for all inputs A and B: A + B == B + A

  17. Parsing, printing, and validating Python source have useful properties

  18. print . parse = id

  19. Shrinking can find minimal counter-examples

  20. You don't need to remember the whole language

  21. Random generation is great for poking programming languages

  22. ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((

  23. CORRECTNESS BY CORRECTNESS BY CONSTRUCTION CONSTRUCTION

  24. If we can construct some data, then that data is correct (by some measure)

  25. Incorrect = type error

  26. Syntactically correct by construction

  27. Python syntax isn't very straightforward

  28. data Expr = Int Int | Bool Bool | Var String | ... data Statement = Assign Expr Expr | ...

  29. Assign (Int 1) (Int 2) 1 = 2

  30. data Expr = Int Int | Bool Bool | Var String | ... data AssignableExpr = AEVar String | ... data Statement = Assign AssignableExpr Expr | ...

  31. {-# language GADTs, DataKinds, KindSignatures #-}

  32. data Assignable = IsAssignable | NotAssignable data Expr :: Assignable -> * where Int :: Int -> Expr 'NotAssignable Bool :: Bool -> Expr 'NotAssignable Var :: String -> Expr a ... data Statement = Assign (Expr 'IsAssignable) (Expr 'NotAssignable) | ...

  33. expr :: Parser (Expr ??)

  34. exprAssignable :: Parser (Expr 'Assignable) exprNotAssignable :: Parser (Expr 'NotAssignable)

  35. data ExprU = IntU Int | BoolU Bool | VarU String | ... data StatementU = AssignU ExprU ExprU | ...

  36. expr :: Parser ExprU statement :: Parser StatementU

  37. validateExprAssignable :: ExprU -> Either SyntaxError (Expr 'Assignable) validateExprNotAssignable :: ExprU -> Either SyntaxError (Expr 'NotAssignable) validateStatement :: StatementU -> Either SyntaxError Statement

  38. Rinse and repeat

  39. But it (mostly) worked... until...

  40. not(condition)

  41. data Expr :: type_stuff -> * where Not :: {- not -} NonEmpty Whitespace -> Expr type_stuff -> Expr type_stuff Parens :: Expr type_stuff -> Expr type_stuff ...

  42. NonEmpty Whitespace -> [Whitespace]

  43. data Expr :: type_stuff -> * where Not :: {- not -} [Whitespace] -> Expr type_stuff -> Expr type_stuff Parens :: Expr type_stuff -> Expr type_stuff ...

  44. Not [] (Parens condition) not(condition)

  45. Not [] (Not [] (Parens condition)) notnot(condition)

  46. Spaces are required between tokens when their concatenation would give a single token

  47. mkNot :: {- not -} [Whitespace] -> Expr type_stuff -> Either SyntaxError (Expr type_stuff)

  48. _Not :: Prism' Expr ([Whitespace], Expr)

  49. _Not :: Prism Expr ExprU ([Whitespace], Expr) ([Whitespace], ExprU)

  50. Expr -> ExprU

  51. CONCRETE SYNTAX CONCRETE SYNTAX TREE TREE

  52. Have the data structures mirror the syntax

  53. Syntax is not code

  54. THE DRAWING THE DRAWING BOARD BOARD

  55. Correct by Construction

  56. Concrete Syntax Tree

  57. Validated/Unvalidated Trees

  58. data Expr (ts :: [*]) = Int Int | Bool Bool | Var String | Not (Expr ts) | ... data Statement (ts :: [*]) = Assign (Expr ts) (Expr ts) | ...

  59. -- raw, unvalidated Expr '[] -- syntax validated Expr '[Syntax] -- indentation & syntax validated Statement '[Indentation, Syntax]

  60. data Indentation validateStatementIndentation :: Statement ts -> Either SyntaxError (Statement (Nub (Indentation ': ts)))

  61. _Not :: Prism (Expr ts) (Expr '[]) ([Whitespace], Expr ts) ([Whitespace], Expr '[])

  62. import Data.Coerce unvalidateStatement :: Statement ts -> Statement '[] unvalidateStatement = coerce

  63. Making 'incorrect' things impossible vs. Making 'correct' things trivial

  64. Modelling how things appear vs. Modelling what things mean

  65. SUMMARY SUMMARY

  66. Property testing is great

  67. We can get pretty far with type-level programming...

  68. ...But it's better to err on the side of usability

  69. Instead of making the incorrect impossible, make the correct trivial

  70. Figure out abstractions, rather than sticking to appearances

  71. Mistakes are probably necessary

  72. COOL STUFF COOL STUFF

  73. fact_tr :: Statement '[] fact_tr = def_ "fact" [p_ "n"] [ def_ "go" [p_ "n", p_ "acc"] [ ifElse_ ("n" .== 0) [return_ "acc"] [return_ $ call_ "go" [p_ $ "n" .- 1, p_ $ "n" .* "acc"]] ] , return_ $ call_ "go" [p_ "n", p_ 1] ]

  74. def fact(n): def go(n, acc): if n == 0: return acc else: return go(n - 1, n * acc) return go(n, 1)

  75. optimizeTailRecursion :: Statement '[] -> Maybe (Statement '[])

  76. rewrite optimizeTailRecursion fact_r :: Statement '[] -> Statement '[]

  77. def fact(n): def go(n, acc): n__tr = n acc__tr = acc __res__tr = None while True: if n__tr == 0: __res__tr = acc__tr break else: n__tr__old = n__tr acc__tr__old = acc__tr n__tr = n__tr__old - 1 acc__tr = n__tr__old * acc__tr__old return __res__tr return go(n, 1)

Recommend


More recommend