You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-[Deriving Instead Of Mutating](#deriving-instead-of-mutating)
@@ -72,6 +73,8 @@ The following is a partial exploration of what I've been imagining for awhile. T
72
73
*[Loops and Comprehensions](#loops-and-comprehensions)
73
74
-[Tagged Comprehensions](#tagged-comprehensions)
74
75
*[Monads](#monads)
76
+
-[The Monad Laws](#the-monad-laws)
77
+
-[Do Syntax](#do-syntax)
75
78
*[Type Annotations](#type-annotations)
76
79
77
80
### Imports
@@ -546,7 +549,7 @@ def greeting: ?(myName){
546
549
greeting; // "Hello!"
547
550
```
548
551
549
-
However, if no pattern matches, the default result of the expression is a Maybe@None -- **Foi** can be configured to issue a warning notice in such a case. More on monads later.
552
+
However, if no pattern matches, the default result of the expression is a Maybe::None -- **Foi** can be configured to issue a warning notice in such a case. More on monads later.
550
553
551
554
To explicitly define a default pattern, use `?:` (which must be the last clause in the pattern matching expression):
552
555
@@ -646,7 +649,9 @@ def myName: "Kyle";
646
649
647
650
### Records And Tuples
648
651
649
-
Records are immutable collections of values, delimited by `< >`. You can name each field of a record, but if you omit a name, numeric indexing is automatically applied. Any record with all numerically indexed fields (implicitly or explicitly defined) is a special case called a Tuple.
652
+
Records are immutable collections of values, delimited by `< >`.
653
+
654
+
You can name each field of a record, but if you omit a name, numeric indexing is automatically applied. Any record with all numerically indexed fields (implicitly or explicitly defined) is a special case called a Tuple.
To keep Record/Tuple syntax complexity to a minimum, *only* the `| |` form of evaluation-expression (function invocation, operators, etc) is allowed inside the `< >` literal definition.
700
+
**Note:**To keep Record/Tuple syntax complexity to a minimum, *only* the `| |` form of evaluation-expression (function invocation, operators, etc) is allowed inside the `< >` literal definition.
696
701
697
702
Strings are just syntax sugar for tuples of characters. Once defined, a string and a tuple of characters will behave the same.
698
703
@@ -750,9 +755,33 @@ As shown, the `<{ }>` *def-block* can contain any arbitrary logic for determi
750
755
751
756
Inside a `<{ }>`*def-block*, the `#` sigil indicates a self-reference to the current Record/Tuple context that's being defined, and can be used either in l-value (assignment target) or r-value (value expression) positions. However, these special self-references cannot cross inner-function boundaries.
752
757
758
+
#### Equality Comparison
759
+
760
+
Since Records/Tuples are primitive (and immutable) value types in **Foi**, equality comparison is structural (meaning deep contents comparison rather than reference identity).
761
+
762
+
```java
763
+
def a:<4, 5, 6>;
764
+
def b:<4, 5, 6>;
765
+
def c:<5, 4, 6>;
766
+
767
+
a ?= b; // true
768
+
b ?= c; // false
769
+
c ?= a; // false
770
+
```
771
+
772
+
```java
773
+
def a:< one:"hello", two:"world">;
774
+
def b:< one:"hello", two:"world">;
775
+
def c:< two:"world", one:"hello">;
776
+
777
+
a ?= b; // true
778
+
b ?= c; // true
779
+
c ?= a; // true
780
+
```
781
+
753
782
#### Inspecting
754
783
755
-
You can determine if a value is in a Tuple with the `?in` / `!in` operator:
784
+
You can determine if a value is *in* a Tuple with the `?in` / `!in` operator:
All syntax rules of Tuples `< >` still apply inside the `<[ ]>`, including use of the `&` and `%` sigils; as Sets are Tuples, not Records, field names are not allowed.
974
+
All syntax rules of Tuple definition `< >` still apply inside the `<[ ]>`, including use of the `&` and `%` sigils; as Sets *are* Tuples, not Records, field names are not allowed.
946
975
947
976
The `+` operator, when both operands are Tuples, acts as a unique-only Set-append operation:
**Warning** The `+` operator only works on Tuples (Sets), not Records.
958
987
988
+
Set equality comparison deserves special attention. Since Sets are merely a construction form for Tuples, the `?=` will perform Tuple equality comparison, where order matters. This may produce undesired results (false negatives).
989
+
990
+
As such, the `?$=` (set equality) operator, and the corresponding `!$=` (set non-equality), perform unordered comparison of Sets (Tuples):
991
+
992
+
```java
993
+
def set1:<[ 4, 5, 5, 6 ]>; // < 4, 5, 6 >
994
+
def set2:<[ 5, 5, 6, 4 ]>; // < 5, 6, 4 >
995
+
def set3:<[ 6, 4, 5, 0 ]>; // < 6, 4, 5, 0 >
996
+
997
+
set1 ?= set2; // false
998
+
999
+
set1 ?$= set2; // true
1000
+
set1 !$= set3; // true
1001
+
```
1002
+
1003
+
**Note:** Unordered (Set equality) comparison is slower than ordered comparison (Tuple equality). This cost is worth paying if you really need to compare two Sets, but it may be worth examining if a different approach is feasible.
1004
+
959
1005
### Functions
960
1006
961
1007
To define a function, use the `defn` keyword. To return a value from anywhere inside the function body, use the `^` sigil:
You can read/interpret the `![x ?> 10]: empty` pre-condition as: "x must be greater than 10; if it's not, return `empty` instead". That's basically the way we interpret pre-conditions in any programming language.
1100
1146
1101
-
**Note:** In this usage, `empty` indicates to the calling code that the function had no valid computation to perform. However, there are other types of values that could (should!?) be returned here, such as a Maybe@None or an Either@Left. More on monads later.
1147
+
**Note:** In this usage, `empty` indicates to the calling code that the function had no valid computation to perform. However, there are other types of values that could (should!?) be returned here, such as a Maybe::None or an Either::Left. More on monads later.
1102
1148
1103
1149
----
1104
1150
@@ -1560,14 +1606,23 @@ defn sub(x,y) ^x - y;
1560
1606
1561
1607
The identity monad in **Foi** is called `Id`, and the empty monad is called `None`.
1562
1608
1563
-
The `@` operator applies the "unit constructor" for any monad type, thus a monad value can be expressed like this:
1609
+
The `@` operator applies the "unit constructor" for any monad type, thus a monadic value can be expressed like this:
1564
1610
1565
1611
```java
1566
1612
def m: Id @ 42; // Id{42}
1567
1613
| @ Id 42 |; // Id{42}
1568
1614
1569
-
def nil: None@; // None{}
1570
-
| @ None |; // None{}
1615
+
def nil: None@; // None
1616
+
| @ None |; // None
1617
+
```
1618
+
1619
+
A monadic value is a primitive, immutable value type in **Foi**, meaning equality comparison is structural (just like Records/Tuples). As such:
1620
+
1621
+
```java
1622
+
def m: Id @ 42;
1623
+
def g: Id @ 42;
1624
+
1625
+
m ?= g; // true
1571
1626
```
1572
1627
1573
1628
If `@` is partially applied, you get a regular function for constructing a specific monad type:
@@ -1590,10 +1645,12 @@ def m: Id @ 21;
1590
1645
1591
1646
m ~map double; // Id{42}
1592
1647
m ~filter isOdd; // Id{21}
1593
-
m ~filter isEven; // None{}
1648
+
m ~filter isEven; // None
1594
1649
m ~fold id; // 21
1595
1650
```
1596
1651
1652
+
**Note:** The `~map` comprehension expresses Functor behavior, and the `~fold` comprehension expresses Foldable behavior; these are related (but distinct) to monads and algebraic structures.
1653
+
1597
1654
In addition to the standard comprehensions, monads (of course!) also can also be used with the `~bind` comprehension:
1598
1655
1599
1656
```java
@@ -1608,6 +1665,52 @@ m ~. (double +> Id @); // Id{42}
1608
1665
1609
1666
**Note:** For convenience/familiarity sake, `~.` and `~chain` are both aliases for the `~bind` comprehension; all 3 are interchangable.
1610
1667
1668
+
`None` exposes a no-op `~.` bind operation:
1669
+
1670
+
```java
1671
+
defn double(v) ^v * 2;
1672
+
1673
+
Id @ 21 ~. double ~fold log; // Id{42}
1674
+
None@ ~. double ~fold log; //
1675
+
```
1676
+
1677
+
Neither the `double()` invocation nor the `log()` invocation will happen for the `None@` monadic value.
1678
+
1679
+
#### The Monad Laws
1680
+
1681
+
For completeness sake, let's illustrate the 3 monad laws using the `Id` monad, the `@` unit-constructor, and the `~.` *bind* operator:
1682
+
1683
+
1.**LeftIdentity:**
1684
+
1685
+
```java
1686
+
defn incM(v) ^(Id@ v +1);
1687
+
defn doubleM(v) ^(Id@ v *2);
1688
+
1689
+
(Id@41) ~. incM; // Id{42}
1690
+
```
1691
+
1692
+
2.**RightIdentity:**
1693
+
1694
+
```java
1695
+
Id@42~. (Id@);
1696
+
// Id{42}
1697
+
```
1698
+
1699
+
3.**Associativity:**
1700
+
1701
+
```java
1702
+
defn incM(v) ^(Id@ v +1);
1703
+
defn doubleM(v) ^(Id@ v *2);
1704
+
1705
+
Id@20~. incM ~. doubleM; // Id{42}
1706
+
1707
+
Id@20~. (v) {
1708
+
incM(v) ~. doubleM;
1709
+
}; // Id{42}
1710
+
```
1711
+
1712
+
#### DoSyntax
1713
+
1611
1714
Composing multiple *bind* steps together can get hairy if subsequent steps need access to the results from earlier steps:
0 commit comments