$git clone https://github.com/Gabriella439/graceGrace (short for Fall-from-Grace) is a domain-specific programming language for prompting models. In particular, Grace is well-suited for building and auto-generating elaborate prompt chains
| 1 | # Grace |
| 2 | |
| 3 | [](https://garnix.io/repo/Gabriella439/grace) |
| 4 | [](https://bsky.app/profile/trygrace.dev) |
| 5 | |
| 6 | Grace (short for [Fall-from-Grace](#name)) is a domain-specific programming |
| 7 | language for prompting models. In particular, Grace is well-suited for building |
| 8 | and auto-generating elaborate prompt chains |
| 9 | |
| 10 | You can use Grace in your browser without installing anything by visiting |
| 11 | [trygrace.dev](https://trygrace.dev/). That website includes an interactive |
| 12 | tutorial and is the recommended way to both learn and get started with using |
| 13 | Grace. |
| 14 | |
| 15 | ## Features |
| 16 | |
| 17 | - Integrated language support for models |
| 18 | |
| 19 | You don't need to install any dependencies or import anything to get started. |
| 20 | Everything you need is built directly into the language. |
| 21 | |
| 22 | The language provides a built-in `prompt` function for prompting a model: |
| 23 | |
| 24 | ```haskell |
| 25 | >>> let key = ./openai.key : Key |
| 26 | |
| 27 | >>> prompt{ key, text: "Generate a list of names" } |
| 28 | " |
| 29 | Here are 40 varied first names (mixed genders and cultures): |
| 30 | |
| 31 | - Aiden |
| 32 | - Sofia |
| 33 | - Mateo |
| 34 | … |
| 35 | - Mabel |
| 36 | - Imani |
| 37 | - Zane |
| 38 | |
| 39 | Want names filtered by gender, culture, style (modern/vintage), or as full names/surnames?" |
| 40 | ``` |
| 41 | |
| 42 | … and you can structure the output by giving a type annotation: |
| 43 | |
| 44 | ```haskell |
| 45 | >>> prompt{ key, text: "Generate a list of names" } : List Text |
| 46 | [ "Ava Thompson" |
| 47 | , "Liam Patel" |
| 48 | , "Sophia Martinez" |
| 49 | … |
| 50 | , "Jackson Rivera" |
| 51 | , "Zoe Wilson" |
| 52 | , "Aiden Park" |
| 53 | ] |
| 54 | ``` |
| 55 | |
| 56 | If the type is sufficiently self-explanatory, you can even omit the prompt: |
| 57 | |
| 58 | ```haskell |
| 59 | >>> prompt{ key } : List { name: Text } |
| 60 | [ { "name": "Alice" } |
| 61 | , { "name": "Bob" } |
| 62 | , { "name": "Charlie" } |
| 63 | , { "name": "Diana" } |
| 64 | , { "name": "Evan" } |
| 65 | ] |
| 66 | ``` |
| 67 | |
| 68 | In fact, you can omit the type, too, if the type can be inferred from use: |
| 69 | |
| 70 | ```haskell |
| 71 | >>> for { name } of prompt{ key } in "Hello, ${name}!" |
| 72 | [ "Hello, Alice!" |
| 73 | , "Hello, Bob!" |
| 74 | , "Hello, Carol!" |
| 75 | , "Hello, Dave!" |
| 76 | , "Hello, Eve!" |
| 77 | ] |
| 78 | ``` |
| 79 | |
| 80 | - JSON schemas inferred from use |
| 81 | |
| 82 | That last example works even without a prompt, schema, or type because Grace's |
| 83 | type checker reasons backwards from how the output is used to infer the |
| 84 | correct JSON schem, like this: |
| 85 | |
| 86 | - the type checker infers that the `name` variable must be `Text` |
| 87 | |
| 88 | … because the `name` variable is interpolated into `"Hello, ${name}!"` |
| 89 | |
| 90 | - the type checker infers that the `prompt` function must generate a `List` |
| 91 | |
| 92 | … because the program loops over the output using a `for … of` loop. |
| 93 | |
| 94 | - the type checker infers each element of the `List` has type `{ name: Text }` |
| 95 | |
| 96 | … because the `for … of` loop destructures each element using `{ name }` |
| 97 | |
| 98 | - therefore the `prompt` function outputs a value of type `List{ name: Text }` |
| 99 | |
| 100 | … which you can read as "a `List` of records, each of which has a `name` field |
| 101 | containing `Text`". |
| 102 | |
| 103 | The interpreter then converts that Grace type into the following matching JSON |
| 104 | schema to constrain the model's output: |
| 105 | |
| 106 | ```json |
| 107 | { |
| 108 | "type": "array", |
| 109 | "items": { |
| 110 | "type": "object", |
| 111 | "properties": { |
| 112 | "name": { |
| 113 | "type": "string" |
| 114 | } |
| 115 | }, |
| 116 | "required": ["name"], |
| 117 | "additionalProperties": false |
| 118 | } |
| 119 | } |
| 120 | ``` |
| 121 | |
| 122 | Finally, the model infers from that JSON schema alone (without any additional |
| 123 | prompt) that it should generate a JSON-encoded list of names. |
| 124 | |
| 125 | - Code generation |
| 126 | |
| 127 | You can prefix the `prompt` keyword with `import` to ask the model to generate |
| 128 | a Grace expression of any type. For example: |
| 129 | |
| 130 | ```haskell |
| 131 | >>> import prompt{ key, text: "increment" } |
| 132 | \n -> n + 1 |
| 133 | ``` |
| 134 | |
| 135 | You can use an explicit type annotation to guide the generated code: |
| 136 | |
| 137 | ```haskell |
| 138 | >>> import prompt{ key, text: "increment" } : { input: Natural } -> { output: Natural } |
| 139 | \{ input } -> { "output": input + 1 } |
| 140 | ``` |
| 141 | |
| 142 | … and if the type is informa |