Skip to content

Commit

Permalink
tweaked grammar for abbreviated else-clause in pattern matching, and …
Browse files Browse the repository at this point in the history
…added another intro snippet of example code to README
  • Loading branch information
getify committed Dec 3, 2023
1 parent 7eb9c6f commit 2b2880c
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 9 deletions.
16 changes: 10 additions & 6 deletions Foi-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,8 @@ def greeting: ?(myName){
greeting; // "Hello!"
```

The `?:` else-clause can also be abbreviated as `:` if preferred. More on that in a bit.

**Note:** Comparing this example to the previous one, `?:` is equivalent to the `!["Kyle"]` pattern. Readability preferences may dictate either style, depending on the circumstances.

A dependent style pattern can include a `,` comma separated list of multiple values, any of which may match the topic:
Expand Down Expand Up @@ -907,7 +909,7 @@ greeting; // "Hello!"

**Note:** The pattern-match conditional `![myName ?= "Kyle"]` is equivalent to `?[myName != "Kyle"]`. Readability preferences may dictate either style, depending on the circumstances.

Just as with dependent pattern matching, it's preferable for the overall independent pattern matching expression to be determinate, in that all conditional branches are covered. Again, to define a default (final) clause, `?:` may be used:
Just as with dependent pattern matching, it's preferable for the overall independent pattern matching expression to be determinate, in that all conditional branches are covered. Again, to define a default (final) clause, `?:` (or abbreviated `:`) may be used:
```java
def myName: "Kyle";
Expand All @@ -930,13 +932,13 @@ Pattern-matching conditional clauses may optionally skip the leading `?` type-sp
?{
[isLoggedIn()]: showDashboard();
[isRegistered()]: showLogin();
default: showRegistration()
: showRegistration()
};
```

Here, the two `[ .. ]: ..` clauses skip the leading type-signifier (a `?` is assumed). However, the `!` type signifier is never assumed, and therefore must be explicitly stated for clauses.
Here, the two `[ .. ]: ..` clauses skip the leading type-signifier (a `?` is assumed). However, the `!` type signifier is never assumed, and therefore must be explicitly stated for clauses. As mentioned earlier, the `:` by itself on the third line illustrates an abbreviated `?:` else-clause.

In cases where both affirmative and negative clauses are present, it may be desirable (for visual consistency) to specify both the `?` and `!` signifiers on the respective clauses, rather than only the `!` being present and the `?` being omitted. For example:
In cases where both affirmative and negative clauses are present, it may be desirable (for visual consistency) to specify both the `?` and `!` signifiers on the respective clauses, rather than only the `!` being present and the `?` being assumed. For example:

```java
// style 1
Expand Down Expand Up @@ -965,16 +967,18 @@ def myName: "Kyle";

// full pattern matching expression:
?{
?[!empty myName]: printGreeting(myName)
[!empty myName]: printGreeting(myName)
}

// standalone guard expression:
?[!empty myName]: printGreeting(myName);

// or:
// equivalent, if you prefer:
![?empty myName]: printGreeting(myName);
```

The leading `?` is required on standalone guard expressions, unlike the optional abbreviated form in pattern matching.

## Records And Tuples

Records are immutable collections of values, delimited by `< >`.
Expand Down
2 changes: 1 addition & 1 deletion Grammar.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ IndepPatternStmt := IndepCondClause MatchConsequent (WhSp* ";")*;
IndepCondClause := ("?" | "!" | Epsilon) BracketExpr;
MatchConsequentNoSemi := ":" WhSp* ExprAsOpt | BlockExpr;
MatchConsequent := ":" WhSp* ((ExprAsOpt WhSp* ";") | BlockExpr);
ElseStmt := "?" MatchConsequentNoSemi (WhSp* ";")*;
ElseStmt := "?"? MatchConsequentNoSemi (WhSp* ";")*;
DepMatchExpr := "?(" WhSp* ExprNoBlockAsOpt WhSp* "){" WhSp* DepPatternStmts WhSp* "}";
DepPatternStmts := DepPatternStmtNoSemi | ((DepPatternStmt WhSp*)+ (ElseStmt | DepPatternStmtNoSemi)?) | ElseStmt;
DepPatternStmtNoSemi := DepCondClause MatchConsequentNoSemi;
Expand Down
39 changes: 37 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,42 @@ defn greetings(who) {

The language is designed for general application programming purposes, but with a clear emphasis on FP (and de-emphasis on OOP). It's not trying to compete in performance or capability with systems languages like C or Rust. Eventually, **Foi** will compile to WASM so it should be usable for applications in a variety of environments, from the web to the server to mobile devices.

An important design goal is that a somewhat experienced developer -- especially one with both FP and imperative experience/curiosity -- should be able to sufficiently/fully learn **Foi** in a few days. It also shouldn't take reading thousands of pages of books or watching months of workshop videos to fully grasp the surface area of **Foi**.
An important design goal is that a somewhat experienced developer -- especially one with both FP and imperative experience/curiosity -- should be able to sufficiently or fully learn **Foi** in several days.

Hopefully, without too much heavy orientation, code like this will settle into familiarity for you:
In the following code snippet, you'll might recognize familiar mechanisms like function calls and pattern recognition. There's also some standard FP idioms like partial application and composition (and a monad!). You might also spot a fun trick (operators-as-functions)!

```java
def adder: arithmetic("add");
def subtractor: arithmetic("subtract");
def tripler: (*)|3|;
def add3: adder|3|;
def sub5: subtractor|,5|;
def compute: tripler +> add3 +> sub5;

adder(3, 4); // 7
add3(4); // 7
subtractor(12, 5); // 7
sub5(12); // 7

3 #> tripler #> adder(3, #) #> sub5; // 7

compute(3); // 7
(<+)(sub5, add3, tripler)(3); // 7

// ***********************

defn arithmetic(op) ^(
?(op){
["add"]: (+);
["subtract"]: (-);
["multiply"]: (*);
["divide"]: (/);
: Left@ "Invalid"
}
)
```

It shouldn't take reading thousands of pages of books or watching months of workshop videos to fully grasp the surface area of **Foi**. Hopefully, without too much learning and practice, even more advanced code like this will brighten into clarity:

```java
defn getFavoriteMovies(userID) ^(IO ~<< {
Expand All @@ -43,6 +76,8 @@ defn getFavoriteMovies(userID) ^(IO ~<< {
getFavoriteMovies(123).run(document)
```

**Hint:** The above snippet defines a function using the "do-syntax" against the `IO` monad, where the `::` definitions are monadic chain operations. Don't worry if that's just a bowl of word-soup for now. You'll *get it* before too long!

## TL;DR

If you're already convinced and ready to jump in, you may want to check these out next:
Expand Down

0 comments on commit 2b2880c

Please sign in to comment.