├── README.md └── proposed ├── modules.md ├── standard-library.md ├── strings.md └── syntax.md /README.md: -------------------------------------------------------------------------------- 1 |

2 | Eve logo 3 |

4 | 5 | # Eve Requests for Comments (RFC) 6 | 7 | RFCs are meant to be an informal communication for starting a discussion on a particular feature, design, protocol, process, or anything else relating to Eve and the Eve community. 8 | 9 | ### Under Development 10 | 11 | Our community is still in the early stages of development, so we're still figuring out how we want this process to work. Most likely, the RFC process will continually evolve to accommodate the needs of the Eve community as it grows. 12 | 13 | # Proposed RFCs 14 | 15 | - [Syntax](https://github.com/witheve/rfcs/blob/master/proposed/syntax.md) - [Discussion](https://github.com/witheve/rfcs/issues/4) 16 | - [Strings](https://github.com/witheve/rfcs/blob/master/proposed/strings.md) - [Discussion](https://github.com/witheve/rfcs/issues/5) 17 | 18 | # When to write an RFC 19 | 20 | There is no one "test" that elevates a pull request to an RFC. However, one good rule is that an RFC is appropriate when the proposal affects disparate areas of the Eve community, especially across team boundaries. For example, a change to the syntax would affect everything from the parser to user programs, so it would certainly warrant an RFC. 21 | 22 | By contrast, an RFC would not be appropriate for a small bugfix, or largely cosmetic changes. 23 | 24 | # The RFC Process 25 | 26 | We're trying to keep this process pretty informal to encourage as much participation as possible, but we also want to have some mechanisms in place to keep things organized and running smoothly. Here is an overview of how the RFC process it works: 27 | 28 | 1. A community member submits an RFC in the form of a pull request to this repository. Proposed RFCs should be placed in the "proposed" folder. 29 | 2. The Eve community will review and categorize the RFC. As long as the pull request is not a duplicate, it will be accepted and merged into this repository, and an issue will be opened to discuss the RFC. 30 | 3. The relevant stakeholders concerning the RFC will be identified and invited to discuss the RFC. 31 | 3. All stakeholders will discuss the RFC, and attempt to build consensus and integrate changes into the RFC. 32 | 4. Once consensus is reached, the RFC will either be accepted or rejected. An accepted RFC will be moved into the "accepted" folder of this repository. A rejected RFC will be removed from the "proposed" folder and its corresponding issue will be marked as closed. 33 | 34 | An accepted RFC means that the proposal is on its way to becoming part of Eve, but the serious work of implementing the feature is still ahead. 35 | 36 | # How to write an RFC 37 | 38 | There is no particular format or length for an RFC, but the following section headers are a good place to start: 39 | 40 | 1. Summary 41 | 2. Motivation 42 | 3. Design 43 | 4. Implementation 44 | 5. Drawbacks 45 | 6. Alternatives 46 | 7. Risks 47 | 48 | Again, feel free to use any or all of these sections, or add your own as you see fit. 49 | -------------------------------------------------------------------------------- /proposed/modules.md: -------------------------------------------------------------------------------- 1 | # Data and code modularity through "contexts" 2 | 3 | ## Summary 4 | 5 | In this RFC we present our idea for how modules work in Eve. Modularity has a spectrum of meanings, ranging anywhere from a collection of functions to package management systems. Modularity in this context refers to a collection of code that can be packaged, distributed, and referenced in other Eve programs to enable code reuse. 6 | 7 | ## Motivation 8 | 9 | In order for Eve to succeed, Eve programs cannot exist in isolation. That is to say, Eve programmers should be able to leverage work done by the Eve community to accelerate the development of their own applications. To do this, programmers need a way to pull in the work of others into their own projects. 10 | 11 | - Reuse of code 12 | - Reuse of data 13 | - Encapsulation 14 | 15 | ## Design 16 | 17 | ### Terminology 18 | 19 | Context 20 | Alternatives: module, library, bag, bucket, batch, container, volume, dataset 21 | 22 | ### Design Goals 23 | 24 | ### Semantics 25 | 26 | Contexts are containers of facts and code. 27 | 28 | - Composition 29 | - Creating contexts that are compositions vs runtime composition 30 | - Types 31 | - Completely isolated loading of a context 32 | - Write isolated loading, including rebinding 33 | - Completely merged 34 | - Data only? 35 | - Code only? 36 | - Logs 37 | - Integrity constraints 38 | 39 | - Built-in contexts 40 | - session 41 | - global 42 | - browser 43 | - file 44 | 45 | ### Syntax 46 | 47 | ``` 48 | // match against all bags in the default composition 49 | match 50 | // match against only galaga 51 | match @galaga 52 | // match against galaga and instance 53 | match (@galaga, @instance) 54 | // match against math and the default composition 55 | match (@math, @default) 56 | ``` 57 | 58 | ``` 59 | // bind into session, these are equivalent 60 | bind 61 | bind @session 62 | // bind into galaga 63 | bind @galaga 64 | // bind into both galaga and instance 65 | bind (@galaga, @instance) 66 | 67 | // commit into session, these are equivalent 68 | commit 69 | commit @session 70 | // commit into galaga 71 | commit @galaga 72 | // commit into galaga and instance 73 | commit (@galaga, @instance) 74 | ``` 75 | 76 | ``` 77 | bind 78 | [#context @galaga url: ""] 79 | ``` 80 | 81 | ### Changes to the runtime 82 | 83 | - Match can optionally take a set of contexts to search in. If none is given, we use the default composition. 84 | - Bind/Commit can optionally take a set of contexts to write to. If none is given, we use the default composition. 85 | - We can load a context by creating a `[#context]` record. 86 | - contexts can be added to the default composition through attributes on a context record. 87 | - Every context internally has both bound and committed facts, which means each user-level context is two internal contexts. 88 | 89 | ## Implementation 90 | 91 | ## Drawbacks 92 | 93 | ## Alternatives 94 | 95 | ## Risks -------------------------------------------------------------------------------- /proposed/standard-library.md: -------------------------------------------------------------------------------- 1 | # Standard Library 2 | 3 | ## Summary 4 | 5 | In this RFC we present our idea for the standard library in Eve. Most languages come with some set of built-in functions to provide basic utility. These functions can include math primitives, string manipulation, time and date utilities, etc. 6 | 7 | ## Motivation 8 | 9 | Our goal for Eve is to be as useful as possible straight out of the box. When you download Eve, you should have everything necessary to go from a blank document to a database-driven dynamic webapp. In order to get there, we need to have a robust standard library -- something that provides functionality that is generally useful to a wide array of domains. 10 | 11 | ## Design 12 | 13 | Eve's standard library will be invisible and included by default. There is no need to import modules. 14 | 15 | Proposed standard library 16 | 17 | - Math - includes math primitives, basic math functions, trigonometry 18 | - Strings - Includes functions that manipulate strings 19 | - Date & Time - Includes functions that provide and manipulate date and time records 20 | - Statistics - Includes functions that calculate statistical measures on records 21 | 22 | ## Implementation 23 | 24 | ### Math 25 | 26 | #### Arithemtic 27 | 28 | - plus (`+`) 29 | - minus (`-`) 30 | - times (`*`) 31 | - divide (`/`) 32 | 33 | #### General Math 34 | 35 | - abs - Absolute value 36 | - ceil - Round a number up 37 | - floor - Round a number down 38 | - round - Round a number 39 | - mod - Modulo division 40 | 41 | #### Trigonometry 42 | 43 | - sin - Sine of an angle 44 | - cos - Cosine of an angle 45 | - tan - Tangent of an angle 46 | 47 | ### Strings 48 | 49 | - length 50 | - concatenate 51 | - replace 52 | - split 53 | - join 54 | 55 | ### Date & Time 56 | 57 | - time - The current system time 58 | - date - The current system date 59 | 60 | ### Statistics 61 | 62 | - mean 63 | - median 64 | - mode 65 | - standard-deviation 66 | - variance 67 | - max 68 | - min 69 | 70 | ## Drawbacks 71 | 72 | A large standard library can be onerous for a small project. 73 | 74 | ## Alternatives 75 | 76 | ## Risks -------------------------------------------------------------------------------- /proposed/strings.md: -------------------------------------------------------------------------------- 1 | Discussion: https://github.com/witheve/rfcs/issues/5 2 | 3 | I'm still trying to build search-as-you-type input with Eve. However, Eve seems to lack any string functions. 4 | 5 | The bare minimum would be to have an expression that checks for substring in a string. Something like this JS function: 6 | 7 | ```js 8 | var contains = (search, string) => string.indexOf(search) !== -1; 9 | ``` 10 | 11 | The most flexible would be to have regexp match expression. Something like this: 12 | 13 | ```js 14 | var matches = (search, string) => !!string.match(new RegExp(search)); 15 | ``` 16 | 17 | The middle ground is to allow prefix-matching for words inside string: 18 | 19 | ```js 20 | var matches = (search, string) => !!string.match(new RegExp('\\b' + search + '\\w*\\b')); 21 | 22 | matches('ab', 'abc def'); // => true 23 | matches('bc', 'abc def'); // => false 24 | matches('de', 'abc def'); // => true 25 | ``` 26 | 27 | The only thing I could currently do is to pre-build the index with external tools, generating huge amount of `[#word-prefix-match]` objects: 28 | 29 | ``` 30 | build the index 31 | freeze 32 | [#word-prefix-match "a" "apple computer"] 33 | [#word-prefix-match "ap" "apple computer"] 34 | [#word-prefix-match "app" "apple computer"] 35 | // … 36 | [#word-prefix-match "c" "apple computer"] 37 | [#word-prefix-match "co" "computer"] 38 | ``` 39 | 40 | And I can't even build this index with Eve code: there are no `split` or `prefix-match` functions. 41 | 42 | P.S. Bonus points is to somehow allow to ignore common words like `a` or `an`, so that `an` wouldn't match `an apple`, but it would match `anne`. 43 | -------------------------------------------------------------------------------- /proposed/syntax.md: -------------------------------------------------------------------------------- 1 | Discussion: https://github.com/witheve/rfcs/issues/4 2 | 3 | # RFC - Developer Syntax 4 | 5 | (A note before we get started: this is our first official Request for Comment (RFC). RFCs are meant to be an informal way to start a discussion on a particular feature, design, protocol, process, or anything else relating to Eve and the Eve community. As the discussion evolves, the RFC will become a form of documentation, explaining the genesis of a particular feature. You can read more about our RFC process [here](https://github.com/witheve/rfcs).) 6 | 7 | ## Summary 8 | 9 | Today we are asking community feedback on our current developer syntax proposal. In this RFC I'll present a complete program written in the syntax and explain how it works. We hope to specifically hear opinions on the following: 10 | 11 | 1. How easy is it to read and understand code you haven't written in the proposed syntax? 12 | 2. How easy is it to write code in the proposed syntax? 13 | 3. Is the programming model clear? 14 | 4. Does the syntax support working and thinking in this model? 15 | 5. What changes would make writing or reading easier? 16 | 6. Did you experience any "ah ha!" moments while reading about the syntax? I.e., was there something we said or an example we gave that made the whole thing click? 17 | 18 | ## Motivation 19 | 20 | In our [Jan/Feb dev diary](http://incidentalcomplexity.com/2016/06/10/jan-feb/) we talked a little about the need for a syntax during our development, even if ultimately most users will never see it. To summarize: while the graphical interface is under development, a textual syntax helps us test the platform, share code, and find/report/reproduce bugs. 21 | 22 | We are specifically calling this a "developer syntax" because it is meant for software developers who know how to program. We want to make sure you understand this isn't how we will present Eve to people who don't know how to program. 23 | 24 | In recent months, the semantics of the Eve language have stabilized to something we're very happy with, and quite excited about. However, the graphical model for interacting with the Eve language is still very much in flux. We've now reached a point where we want to share our work with the community, and we can't do that without a proper interface to the language. 25 | 26 | Thus, to get Eve out to early adopters sooner, we have developed a textual syntax, quite different from what we've [shown so far](http://incidentalcomplexity.com/2016/06/30/apr/). 27 | 28 | ## Design 29 | 30 | ### Design Goals 31 | 32 | Here are the broad design goals we identified when designing the syntax: 33 | 34 | 1. **Habitable** - This syntax is designed for humans, so decisions regarding the ergonomics of the syntax are of primary concern. 35 | 2. **Readable** - Since code is read more than written, we want the syntax to be eminently readable. 36 | 3. **Consistent** - The syntax should be consistent with prior knowledge, so that users unfamiliar with Eve can read an Eve program and figure out what's going on at a high level without explicitly knowing the syntax. 37 | 4. **Distinct** - This one is purposefully in contention with goal (3); we want the syntax to be familiar but not too familiar. If our syntax is too close to other languages (e.g. if we used C-style curly braces), we might project that our semantics are similar, when in fact they are very different. 38 | 39 | ### Programming Model 40 | 41 | To really understand a syntax, you have to also understand the semantics of the underlying programming model, so that's where we will start. This discussion will be very high-level, so don't worry about the details for now. First, remember that Eve is not just a language; it is also a database. These are not separate components that interact; they are actually one and the same. However, sometimes we use "Eve DB" to refer to the underlying facts in the database. 42 | 43 | At its core, Eve only responds to two commands: 44 | 45 | 1. What facts do you know about this "record"? 46 | 2. Remember a new fact about this "record". 47 | 48 | Communication with Eve happens through "records", which are key-value pairs attached to a unique ID. To access facts in the Eve DB, you use a record. To insert/remove facts into/from the Eve DB, you also use a record. 49 | 50 | Computation occurs as a result of relationships between records. For example, I might model myself as a record with an `age` and a `birth year`. There might also be a record representing the `current year`. Then I could compute my `age` as my `birth year` subtracted from the `current year`. 51 | 52 | A key concept here is that age is a derived fact, supported by two other facts: `birth year` and `current year`. If either of those supporting facts are removed from the Eve DB, then `age` can no longer exist as well. For intuition, think about modeling this calculation in a spreadsheet using three cells, and what would happen to the `age` cell if you deleted one of the other two cells. 53 | 54 | Thus the presence or absence of facts can be used to control the flow of a program. To see how this works, consider how a navigation button on a webpage might work. When the button is clicked, a fact is inserted into the Eve DB noting the element that was clicked. This click fact would support a record that defines the page to be rendered. This in turn would support the page renderer, whose job it is to display the page. 55 | 56 | One last thing to note about control flow is that we have no concept of a loop in Eve. Recursion is one way to recover looping, but set semantics and aggregates often obviate the need for recursion. In Eve, every value is actually a set. With operators defined over sets (think `map()`) and aggregation (think `reduce()`) we can actually do away with most cases where we would be tempted to use a loop. 57 | 58 | #### Set Semantics 59 | 60 | One other thing to know about Eve is that records follow [set semantics](https://en.wikipedia.org/wiki/Set_(mathematics)). Sets are collections where every element of the collection is unique. This is in contrast to bag semantics, where elements can be duplicated. We'll see the implications of this later, but it's important to keep in mind. 61 | 62 | ### A Complete Program - Party Planning 63 | 64 | Through the rest of this document, we'll refer to the following complete Eve program. Don't worry about understanding it right now; we'll go over what all the parts mean, and then hopefully the program will become clear. Let's dive right in: 65 | 66 | # Planning my birthday party 67 | 68 | This program figures out who can attend my party, and calculates how many burgers 69 | I need to buy. 70 | 71 | First I invite friends to the party. I can only invite friends who are not busy 72 | on the date of the party. Each one of my friends has marked their busy dates for 73 | me. 74 | 75 | ``` 76 | match 77 | party = [@"my party" date] 78 | friend = [#friend busy-dates != date] 79 | bind 80 | friend += #invited 81 | ``` 82 | 83 | Guests are allowed to bring their spouses, so I have to invite them as well. I 84 | won't invite spouses of friends who can't come to the party though. 85 | 86 | ``` 87 | match 88 | [#invited spouse] 89 | bind 90 | spouse += #invited 91 | ``` 92 | 93 | I'm only serving burgers at my party. Guests can have between 0 and 3 burgers. 94 | Vegetarians don't want any burgers, the standard amount is 1, and `#hungry` 95 | guests get 2. My friend `@Arthur` gets 3 burgers, because he's exceptionally 96 | hungry. I need to keep track of the total number of burgers needed, as well as 97 | how many each guest prefers. 98 | 99 | ``` 100 | match 101 | guest = [#invited name] 102 | party = [@"my party"] 103 | burgers = if guest = [@Arthur] then 3 104 | else if guest = [#hungry] then 2 105 | else if guest = [#vegetarian] then 0 106 | else 1 107 | total-burgers = sum[value: burgers, given: (burgers, guest)] 108 | bind 109 | party.burgers := total-burgers 110 | guest.burgers := burgers 111 | ``` 112 | 113 | Finally, I need to display the guest list and how many burgers I need total. 114 | 115 | ``` 116 | match 117 | [@"my party" burgers] 118 | guest = [#invited name] 119 | burger-switch = if guest.burgers = 1 then "burger" 120 | else "burgers" 121 | bind 122 | [#div children: 123 | [#h1 text: "Guest List"] 124 | [#div text: "{{name}} will eat {{guest.burgers}} {{burger-switch}}" sort: name] 125 | [#h2 text: "Total burgers needed: {{burgers}}"]] 126 | ``` 127 | 128 | ## Data 129 | 130 | The rest of the code establishes the data needed to run the above program. I 131 | instantiate the party here. 132 | 133 | ``` 134 | match 135 | [#session-connect] 136 | commit 137 | [@"my party" date: 2] 138 | ``` 139 | 140 | I also need to add facts about my friends in order for this to all work. 141 | 142 | ``` 143 | bind 144 | [#friend name: "James", busy-dates: 1 #hungry, spouse: [@Sam]] 145 | [#friend name: "Betty", busy-dates: 2, spouse: [@Joe], #hungry] 146 | [#friend name: "Carol", busy-dates: 3 #vegetarian] 147 | [#friend name: "Duncan", busy-dates: 4] 148 | [#friend name: "Arthur", busy-dates: 3, #hungry] 149 | [name: "Sam" busy-dates: 3] 150 | [name: "Joe" busy-dates: 4] 151 | ``` 152 | 153 | ## Results 154 | 155 | Expected result: 156 | 157 | --- 158 | 159 | # Guest List 160 | 161 | Arthur will eat 3 burgers 162 | Carol will eat 0 burgers 163 | Duncan will eat 1 burger 164 | James will eat 2 burgers 165 | Sam will eat 1 burger 166 | 167 | ## Total burgers needed: 7 168 | 169 | **Note: At the moment, this program does not execute correctly. We're working on adding the necessary features** 170 | 171 | #### Program Structure 172 | 173 | The first thing to notice is the broad structure of the program. In the spirit of [literate programming](https://en.wikipedia.org/wiki/Literate_programming), Eve programs are primarily prose, interleaved with Eve code. Donald Knuth explains literate programming in his [influential paper](http://www.literateprogramming.com/knuthweb.pdf): 174 | 175 | > The practitioner of literate programming can be regarded as an essayist, whose main concern is with exposition and excellence of style. Such an author ... strives for a program that is comprehensible because its concepts have been introduced in an order that is best for human understanding, using a mixture of formal and informal methods that reinforce each other. 176 | 177 | This description fits with the ethos of Eve - that programming is primarily meant to communicate with other humans, not the computer. You'll notice the above Eve program is actually written in two languages: Markdown, used to format the prose; and Eve, which is delineated by standard Markdown code blocks. Only the content within a block is compiled, while everything else is disregarded as a comment. 178 | 179 | Writing code this way has several properties that result in higher quality programs: 180 | 181 | - Literate programming forces you to consider a human audience. While this is usually the first step in writing any document, in programming the audience is typically a machine. For an Eve program, the audience might be your collaborators, your boss, or even your future self when revisiting the program in a year. By considering the audience of your program source, you create an anchor from which the narrative of your program flows, leading to a more coherent document. 182 | - The human brain is [wired to engage](https://blog.bufferapp.com/science-of-storytelling-why-telling-a-story-is-the-most-powerful-way-to-activate-our-brains) with and remember stories. Think back to a book you read (or maybe a show you watched) last year. You probably remember in great detail all of the characters and their personalities, the pivotal moments of the plot, the descriptions of the various settings, etc. But how much can you remember of a piece of code you haven't looked at for a year? Literate programming adds another dimension to your code that will help you keep more of your program in working memory. 183 | - Since Eve code blocks can be arranged in any order, literate programming encourages the programmer to arrange them in an way that makes narrative sense. Code can have a beginning, middle, and end just like a short story. Or like an epic novel, code can have many interwoven storylines. Either way, the structure of the code should follow an order imposed by a narrative, not one imposed by the compiler. 184 | - It's often said that you don't really understand something until you explain it to someone else. Literate programming can reveal edge cases, incorrect assumptions, gaps in understanding the problem domain, and shaky implementation details before any code is even written. 185 | 186 | Literate programming is a first-class design concept in Eve. We will be writing all of our programs in this manner, and will encourage others to do the same for the reasons above. That said, there is nothing in the syntax that specifically requires literate programming; you can write your program as a series of code blocks without any prose, and it will be perfectly valid. 187 | 188 | ### Records 189 | 190 | Records are the predominant datatype in Eve. In the proposed syntax, records are a set of attribute:value pairs enclosed in square brackets: 191 | 192 | ``` 193 | record = [attribute1: value1 attribute2: value2 ... attributeN: valueN] 194 | ``` 195 | 196 | Records are essentially pattern matches against the Eve DB, i.e. records ask Eve to find all the entities that match the supplied attribute shape. For example, our first record in the program is `[@"my party" date]`. The resulting record will consist of all the facts matching a `name` attribute with value "my party" and a date attribute with any value. 197 | 198 | The record also binds `date` to the top level `date` variable, accessible outside of the record (but only within the block). If you want to use `date` to mean something else, then you can alias it using the bind operator (see the next section). 199 | 200 | Records can be bound to a variable, e.g. `party = [name: "my party" date]`. This provides a handle to the record, allowing you to access and mutate attributes using dot notation, e.g. `party.date`. 201 | 202 | Records can be nested: 203 | 204 | ``` 205 | record = [attribute: [attribute2: value]] 206 | ``` 207 | 208 | ### Binding, Equivalence, and Names 209 | 210 | Our syntax has two binding operators: colon ( `:` ), and equals ( `=` ). By convention, colon is used within records, and equals is used outside of records. Either way, binding works the same: binding asserts that the left hand side of the operator is equivalent to the right hand side. This is distinct from assignment, which is not a concept in Eve (therefore, we have no need for an `==` operator). 211 | 212 | Names are another way to say one thing is equivalent to another; within a block, variables with the same name represent the same record or attribute of a record. For instance: 213 | 214 | ``` 215 | People who are 50 years old 216 | [tag: "person" age] 217 | age = 50 218 | 219 | The same query as above 220 | [tag: "person", age: 50] 221 | 222 | Never true 223 | [tag: "person", age: 10] 224 | person.age = 20 225 | ``` 226 | 227 | Names are a little more permissive in our syntax than other languages. We allow most symbols in a name (with the exception of space, @, #, //, period, question, comma, colon, and grouping symbols). So operators like `-` and `+` are valid symbols in a name. Furthermore, we support Unicode, so you can include symbols (such as letters from the Greek alphabet). Such permissive naming comes at the cost of requiring whitespace in expressions. For example `friend-age` is a name, whereas `friend - age` is subtracting `age` from `friend`. 228 | 229 | ### Record Names and Tags 230 | 231 | We've identified two attributes that are generally useful, so we've given them special syntax. These attributes are `name` and `tag`. 232 | 233 | #### Name Selector ( `@` ) 234 | 235 | The name selector is used to select a specific named record from the Eve DB. Named records are just records with a `name` attribute specified. In the example program,`[@"my party"]` is shorthand for `[name: "my party"]`. 236 | 237 | #### Tag Selector ( `#` ) 238 | 239 | The tag selector is used for selecting a group of similar records, i.e. records with the same tag attribute. In the above example, we used `[#friend]`, which is shorthand for `[tag: "friend"]`. 240 | 241 | ### Block structure 242 | 243 | Blocks themselves have their own structure. Let's focus in on the first block: 244 | 245 | ``` 246 | match 247 | party = [@"my party" date] 248 | friend = [#friend busy-dates != date] 249 | bind 250 | friend += #invited 251 | ``` 252 | 253 | Each block is written in two phases: `match` then `action`. These phases mirror the two Eve commands outlined earlier. In the `match` phase, we ask Eve for known facts, and we might transform those facts using temporary variables. In the `action` phase we perform some action on the Eve DB to either add or remove facts. Let's look at each of those phases now: 254 | 255 | #### Phase 1: Match 256 | 257 | The `match` phase is used to gather all the information you need to complete your block. The `match` phase is prefaced with the `match` keyword, and can potentially be omitted. 258 | 259 | In the following block, we want to count all the guests coming to the party. To do this, we need the date of the party, a list of all my friends and their availability, and then a count of the guests on the list. Below, I've annotated what's going on in the `match` phase. 260 | 261 | ``` 262 | match 263 | party = [@"my party" date] // Select the party and the date of the party 264 | friend = [#friend busy-dates != date] // Select friends who are not busy during the date of the party 265 | ``` 266 | 267 | Let's take a look at other things you can do in the `match` phase: 268 | 269 | ##### **Aggregates** 270 | 271 | Aggregates are functions that take an input set and produce an output set, typically with a different cardinality than the input set. Aggregates are called just like other functions in Eve. For example, `count` takes an input set of cardinality `N` and produces a set of cardinality `1` as the result. A familiar analogue in other languages is the `reduce()` function. Here is an example of an aggregate in use: 272 | 273 | ``` 274 | total-burgers = sum[value: burgers, given: (burgers, guest)] 275 | ``` 276 | 277 | One important consideration when using an aggregate is the input set. Due to set semantics, the result may not be what you expect if the input set contains duplicate elements. 278 | 279 | Recall that a set is an unordered collection of unique elements. In our example, `burgers = (3, 0, 1, 2, 1)`, which as a set is `{3, 0, 1, 2}`. Thus `sum[value: burgers, given: burgers] = 6`, which is not what we expect. However, when we say `sum[value: burgers, given: (burgers, guest)]` then each burger is associated with a corresponding guest, making each element unique, e.g. `(burgers, guest) = {{3, Arthur}, {0, Carol}, {1, Duncan}, {2, James}, {1, Sam}}`. Summing burgers given this set yields the expected result of `7`, because the duplicated `1` is now unique. 280 | 281 | ##### **If** 282 | 283 | `If` allows conditional equivalence, and works a lot like `if` in other languages. Our `if` has two components: The keyword `if` followed by a conditional; and the keyword `then` followed by one or more return records. An optional `else` keyword indicates the default value: 284 | 285 | ``` 286 | burger-switch = if guest.burgers = 1 then "burger" 287 | else "burgers" 288 | ``` 289 | 290 | This block is used to switch between the singular and plural for displaying the number of burgers each guest is eating. `If` statements can be composed, permitting the creation of complex conditional statements. For instance, instead of inviting friends and their spouses in two blocks (the first two blocks in the example program), I could have done it in a single block using an `if` statement: 291 | 292 | ``` 293 | [@"my party" date] 294 | friend = [#friend busy-dates != date] 295 | guest = if friend then friend 296 | if friend.spouse then friend.spouse 297 | ``` 298 | 299 | This is equivalent to a `union/and` operator, which combines elements from disparate sets into the same set. The second way to use `if` is in conjunction with the `else` keyword: 300 | 301 | ``` 302 | burgers = if guest = [@Arthur] then 3 303 | else if guest = [#hungry] then 2 304 | else if guest = [#vegetarian] then 0 305 | else 1 306 | ``` 307 | 308 | This is equivalent to a `choose/or` operator, selecting only the first branch with a non-empty body. A bug in this program is that if some guest is tagged both `#hungry` and `#vegetarian`, that guest will actually receive two burgers. Therefore, while order of statements usually does not matter in Eve, `if` statements are one area where it does. 309 | 310 | A final feature of the if statement is multiple returns. For instance, we could have done this: 311 | 312 | ``` 313 | (burgers, status) = if guest = [@Arthur] then (3, #fed) 314 | else if guest = [#hungry] then (2, #fed) 315 | else if guest = [#vegetarian] then (0, #needsfood) 316 | else (1, #fed) 317 | ``` 318 | 319 | ##### **Not** 320 | 321 | Not is an anti-join operator, which takes a body of records. For example, we can get a list of people who are not invited to the party: 322 | 323 | ``` 324 | friends not invited to the party 325 | friends = [#friend] 326 | not(friends = [#invited]) 327 | ``` 328 | 329 | ##### **Expressions** 330 | 331 | Expressions are used to perform calculations using constants and attributes of records, e.g. `party.burgers / party.guest-count` would calculate the ratio of burgers to the number of guests. Note that `guest-count` is a variable, not an expression. In expressions, you are required to add whitespace between operators. This helps with readability, but it also allows us to add more characters to names. 332 | 333 | Operators are defined over sets, so you could do something like `cheese-slices = guest.burgers * 2`, which would multiply every guest's burger count by two. There is a pitfall here: if you perform an operation on disjoint sets (they have no attribute in common) with differing cardinality (their sets have a different number of elements), the result will be a [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product) of the two sets. This is usually not a desired behavior, but sometimes it is what you want to do, e.g. if you wanted to calculate the distance from every point in a set to every point in another set, a Cartesian product would be useful. 334 | 335 | ##### **Functions** 336 | 337 | **Note: This section of the proposal is not implemented yet** 338 | 339 | It turns out that functions as they exist in other languages are mostly obviated by Eve's tag semantics. Consider the following two statements 340 | 341 | ``` 342 | x = sin(90) // A typical function call 343 | [#sin deg: 90, return: x] // An Eve record 344 | ``` 345 | 346 | These statements accomplish the same objective, of storing the sine of an angle in a result variable. The Eve syntax is at a disadvantage though, because it cannot be composed into an expression like a typical function. Therefore, we propose the following syntax for functions in Eve: 347 | 348 | ``` 349 | x = sin[deg: 90] 350 | ``` 351 | 352 | which is sugar for: 353 | 354 | ``` 355 | [#sin #function deg: 90, return: x] 356 | ``` 357 | 358 | The return attribute is implicitly the value of `sin[deg]`, so now the record can be used and composed like functions in other languages. We're proposing this syntax for several reasons. 359 | 360 | - Square brackets draw attention to the fact that the function call is nothing more than a regular record. 361 | - Explicit parameters are self-documenting, which makes the code more readable if you're not familiar with the function signature. 362 | - Explicit parameters permit arguments in any order, which makes optional arguments easy to implement. 363 | - Finally, since functions are really just records, you can extend a function so it can be used in new ways. For example, we could extend the `sin` function to support radians: 364 | 365 | 366 | Calculate the sine of an angle given in radians 367 | ``` 368 | match 369 | return = sin[angle: value? * π / 180] 370 | bind 371 | sin[rad: value?, return] 372 | ``` 373 | 374 | The `?` notation here indicates that the value is an input. We can use the extended function like so: 375 | 376 | ``` 377 | x = sin[rad: π / 2] 378 | ``` 379 | 380 | ##### **String Interpolation** 381 | 382 | We support string interpolation (concatenation) using double curly braces embedded in a string, e.g. `"{{ }}"`. In the party program, when we print output, we use string interpolation to format the results: 383 | 384 | ``` 385 | bind 386 | [#div children: 387 | [#h1 text: "Guest List"] 388 | [#div text: "{{name}} will eat {{guest.burgers}} {{burger-switch}}" sort: name] 389 | [#h2 text: "Total burgers needed: {{burgers}}"]] 390 | ``` 391 | 392 | The `div` that prints the guest name is only written once, but because of set semantics, it will be printed as many times as there are elements in the set (in this case we'd expect it to be printed five times). For the same reason, the total burger count will only be printed once. 393 | 394 | #### Phase 2: Action 395 | 396 | The `action` phase of a block indicates that we are changing the Eve DB in some way. This phase is preceded by either the `bind` or `commit` fences (explained below). While the `match` phase can be omitted, omitting the `action` phase is probably an error, i.e. the block doesn't do anything without an `action`. 397 | 398 | The transition to the `action` phase means we're no longer able to use any statements available in the `match` phase, e.g. `if`, `not`, aggregates, expressions, etc. 399 | 400 | ##### Adding and Removing Records 401 | 402 | Records can be added to Eve after an `action` fence: 403 | 404 | ``` 405 | commit 406 | [@"my party" date: 2] 407 | ``` 408 | 409 | Records can be removed from Eve using the `none` keyword. For example, we could remove `@"my party"` like so: 410 | 411 | ``` 412 | commit 413 | [@"my party"] := none 414 | ``` 415 | 416 | ##### Action Operators 417 | 418 | We have four operators for performing actions on records in the Eve DB: add, set, remove and merge: 419 | 420 | - Add ( `+=` ) - adds values to an attribute 421 | - Set ( `:=` ) - sets the value of an attribute 422 | - Remove ( `-=` ) - removes attribute with value from a record 423 | - Merge (`<-`) - merges one record with another 424 | 425 | ###### Add, Set, and Remove 426 | 427 | Add, set and remove all work similarly. On the left hand side of the operator you provide a record and attribute through dot notation. On the right hand side, you provide a value, that will either add to, set, or remove from the left hand side attribute. For example: 428 | 429 | ``` 430 | party.burgers := total-burgers 431 | ``` 432 | 433 | sets the `burgers` attribute on the `party` record to the value `total-burgers`. An exception to this is when the value is a tag or name. In either case, you don't have to specify an attribute on the left hand side. For example, the following adds the tag `#invited` to the `friend` record. 434 | 435 | ``` 436 | friend += #invited 437 | ``` 438 | 439 | Actions follow set semantics. If an attribute exists on a record, using `+=` will just add it to the set. For instance, if `person.age = {10}`, and `person.age += 20`, then `person.age = {10, 20}` (note: the curly braces are not part of the syntax, but are a standard way of indicating a set). 440 | 441 | ###### Merge 442 | 443 | Merge works differently from the other three operators. The purpose of the merge operator is to merge two records together, i.e. the updated record is the set union of the two records. On the left hand side, you just provide a record handle. On the right hand side, you provide a new record. For instance: 444 | 445 | ``` 446 | guest <- [burgers: 3] 447 | ``` 448 | 449 | This merges the new record into `guest`, setting `burgers` to `3`. 450 | 451 | ##### Commit vs. Bind 452 | 453 | We have two fences for the `action` phase, with differing semantics. The `commit` fence tells Eve that any record behind the fence should persist in the database, even if the supporting data in the `match` phase is removed. For example: 454 | 455 | ``` 456 | match 457 | [#session-connect] 458 | commit 459 | [@"my party" date: 2] 460 | ``` 461 | 462 | The use of `commit` here means that if `#session-connect` ever exists in the database, then the record `[@"my party" date: 2]` exists even if `#session-connect` is removed in the future. In fact `#session-connect` exists for only a single tick of compiler time, so if we didn't use `commit`, then the party would only exist for an instant and disappear. 463 | 464 | By contrast, the `bind` fence tells Eve that any record behind the fence is bound to the data in the `match` phase, and therefore only exists if that data exists. For example: 465 | 466 | ``` 467 | match 468 | party = [@"my party" date] 469 | friend = [#friend busy-dates != date] 470 | bind 471 | friend += #invited 472 | ``` 473 | 474 | The behavior of this code is that a `friend` is `#invited` as long as they are not busy during the date of the party. Let's say my friend `@Arthur`'s calendar is initially clear, and so he is originally `#invited`. Then some time later, `@Arthur` suddenly adds the party date to his list of busy dates. Now, he no longer satisfies the conditions of the block. Therefore, Eve removes `#invited` from `@Arthur`, and he no longer shows up on the guest list, which also subsequently lowers the burger count. Had we used the `commit` fence, then his initial invitation would be permanent until we explicitly remove it. 475 | 476 | ##### Commit global 477 | 478 | By default, any changes made to the database are per session. This means any facts added to the database are only visible to the session that added them. `Commit` can be optionally followed by the `global` keyword, which indicates that the fenced records are available globally to all sessions connected to Eve. 479 | 480 | This is useful if you want to create a networked application. For our example, I might ask all my friends to write the following query: 481 | 482 | Hi friends. Please edit the following Eve code for my party planning app. 483 | Just fill in your name, the dates you are busy, and add your spouse as well. 484 | You can also add either of the following tags: #hungry, and #vegetarian. 485 | 486 | ``` 487 | match 488 | [#session-connect] 489 | commit global 490 | [#friend name busy-dates spouse] 491 | ``` 492 | 493 | Now, when my friends execute that block (filled in with their details), their data is available to my party planning application. 494 | 495 | ## Formal Specification 496 | 497 | The following specification is expressed in [Extended Backus–Naur Form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form) 498 | 499 | ### Basics 500 | 501 | ``` 502 | newline = "\n" 503 | whitespace = " " | "\t" | "," | newline; 504 | unicode = ? all unicode chars - whitespace ?; 505 | specials = "@" | "#" | "." | "," | "(" | ")" | "[" | "]" | "{" | "}" | "⦑" | "⦒" | ":" | "\""; 506 | non-special = unicode - specials; 507 | ``` 508 | 509 | ### Values 510 | 511 | ``` 512 | none = "none"; 513 | boolean = "true" | "false"; 514 | numeric = "0" .. "9"; 515 | number = ["-"] {numeric} ["." {numeric}]; 516 | string-interpolation = "{{" expression "}}"; 517 | string = "\"" {string-interpolation | unicode - "\"" | "\\\"" | whitespace} "\""; 518 | uuid ="⦑" (unicode - specials) "⦒" 519 | ``` 520 | 521 | ### Keywords and Identifiers 522 | 523 | ``` 524 | match = "match"; 525 | action = "bind" | "commit"; 526 | if = "if"; 527 | then = "then"; 528 | else = "else"; 529 | is = "is"; 530 | not = "not"; 531 | keyword = match | action | if | then | else | boolean | is | not 532 | non-special-non-numeric = non-special - numeric 533 | identifier = (non-special-non-numeric {non-special}) - keyword - "```"; 534 | ``` 535 | 536 | ### Comparisons 537 | 538 | ``` 539 | equality = ":" | "="; 540 | comparator = equality | ">" | "<" | ">=" | "<=" | "!="; 541 | comparison = expression whitespace+ comparator whitespace+ expression; 542 | ``` 543 | 544 | ### Functions 545 | 546 | ``` 547 | infix-op = "*" | "+" | "-" | "/"; 548 | infix = expression infix-op expression; 549 | function = identifier "[" [attribute] {whitespace+ attribute} "]"; 550 | ``` 551 | 552 | ### Records and attributes 553 | 554 | ``` 555 | record = "[" [attribute] {whitespace+ attribute} "]" 556 | attribute = name | tag | attribute-not | identifier {whitespace+ comparator whitespace+ expression}; 557 | name = "@" (identifier | string); 558 | tag = "#" (identifier | string); 559 | attribute-not = not "(" whitespace* identifier [comparator whitespace+ expression] ")"; 560 | attribute-access = identifier whitespace* {"." whitespace* identifier}+ 561 | ``` 562 | 563 | ### Special forms 564 | 565 | ``` 566 | not-statement = not "(" statement {whitespace+ statement} ")"; 567 | is-expression = is "(" comparison ")"; 568 | ``` 569 | 570 | ### Expression 571 | 572 | ``` 573 | expression = number | identifier | function | infix | record | attribute-access; 574 | ``` 575 | 576 | ### Statements 577 | 578 | ``` 579 | comment = "//" {unicode | whitespace - newline} newline 580 | statement = record | function | not-statement | if-statement | comparison | comment 581 | ``` 582 | 583 | ### Action statements 584 | 585 | ``` 586 | create-action = (identifier whitespace+ equality whitespace+ record) | record 587 | merge-action = (identifier | attribute-access) whitespace+ "<-" whitespace+ record 588 | name-tag-action = (identifier | attribute-access) whitespace+ ("+=" | "-=") whitespace+ (name | tag) 589 | remove-action = (identifier | attribute-access) whitespace+ ":=" whitespace+ none 590 | attribute-action = attribute-access whitespace+ (":=" | "+=" | "-=") whitespace+ expression 591 | action-operation = create-action | merge-action | name-tag-action | remove-action | attribute-action 592 | ``` 593 | 594 | ### If-Then 595 | 596 | ``` 597 | group = "(" expression {whitespace+ expression} ")" 598 | binding-group = "(" identifier {whitespace+ identifier} ")" 599 | if-result = (group | expression); 600 | if-expression = if whitespace+ {statement whitespace+} then whitespace+ if-result; 601 | else-if-expression = else whitespace+ if whitespace+ {statement whitespace+} then whitespace+ if-result; 602 | else-expression = else whitespace+ if-result 603 | if-statement = (identifier | binding-group) whitespace+ equality whitespace+ 604 | if-expression 605 | {whitespace+ (if-expression | else-if-expression)} 606 | [else-expression] 607 | ``` 608 | 609 | 610 | ### Sections 611 | 612 | ``` 613 | context-declaration = name | "(" {name whitespace+} ")" 614 | match-section = match whitespace+ [context-declaration whitespace+] {statement whitespace} 615 | action-section = action whitespace+ [context-declaration whitespace+] {action-statement whitespace} 616 | section = match-sectiong | action-section 617 | ``` 618 | 619 | ### Program and blocks 620 | 621 | ``` 622 | fence-symbol = "```" 623 | start-fence = newline fence-symbol [whitespace* (unicode - newline)] newline 624 | end-fence = newline fence-symbol newline 625 | block = start-fence {section} end-fence 626 | program = {unicode | whitespace | block} 627 | ``` 628 | 629 | ## Other Examples 630 | 631 | Feel free to check out some more examples of programs written in the proposed syntax [here](https://github.com/witheve/eve/tree/master/examples). In particular, the following are complete applications: [chat](https://github.com/witheve/eve/blob/master/examples/chat.eve), [clock](https://github.com/witheve/eve/blob/master/examples/clock.eve), and [todo-mvc](https://github.com/witheve/eve/blob/master/examples/todomvc.eve). 632 | 633 | ## Drawbacks 634 | 635 | One of the lines we are balancing is making the syntax familiar yet different. The proposed syntax is indeed very different from what people are used to. 636 | 637 | Another drawback is having a syntax at all, in that it forces us to maintain two disjoint interfaces for Eve: this textual syntax and a graphical interface for non-programmers. However, given the reasons outlined above, we believe this cost is worth paying. 638 | 639 | ## Alternatives 640 | 641 | We've tried various syntaxes before this one. Earlier this year we had a syntax based on s-expressions, which you can read about [here](http://incidentalcomplexity.com/2016/06/10/jan-feb/) and [here](http://incidentalcomplexity.com/2016/06/22/mar2/). You can also look at our earliest syntax [here](https://github.com/witheve/eve-experiments/tree/syntax/examples). 642 | 643 | ## Risks 644 | 645 | The biggest risk here is that we will soon be introducing Eve to a much wider audience, and this syntax will be the face of Eve. Many people conflate a programming language with a syntax, and so a poor syntax will leave a poor lasting impression of Eve in general. 646 | --------------------------------------------------------------------------------