aboutsummaryrefslogtreecommitdiff
path: root/documentation/book/the_lux_programming_language/appendix_c.md
diff options
context:
space:
mode:
Diffstat (limited to 'documentation/book/the_lux_programming_language/appendix_c.md')
-rw-r--r--documentation/book/the_lux_programming_language/appendix_c.md106
1 files changed, 53 insertions, 53 deletions
diff --git a/documentation/book/the_lux_programming_language/appendix_c.md b/documentation/book/the_lux_programming_language/appendix_c.md
index a2c0953cb..5bfb731ea 100644
--- a/documentation/book/the_lux_programming_language/appendix_c.md
+++ b/documentation/book/the_lux_programming_language/appendix_c.md
@@ -1,26 +1,28 @@
# Appendix C: Pattern-matching macros
-Pattern-matching is a native Lux feature, and yet `case` is a macro.
+Pattern-matching is a native Lux feature, and yet `when` is a macro.
_Why?_, you may wonder. _What does being a macro add to the mix?_
-Well, as it turns out, by making `case` be a macro, Lux can perform some compile-time calculations which ultimately enable a set of really cool features to be implemented: custom pattern-matching.
+Well, as it turns out, by making `when` be a macro, Lux can perform some compile-time calculations which ultimately enable a set of really cool features to be implemented: custom pattern-matching.
Most languages with pattern-matching have a fixed set of rules and patterns for how everything works.
Not so with Lux.
-Lux provides a set of default mechanisms, but by using macros where patterns are located, `case` can expand those macro calls to get the myriad benefits they offer.
+Lux provides a set of default mechanisms, but by using macros where patterns are located, `when` can expand those macro calls to get the myriad benefits they offer.
But enough chit-chat.
Let's see them in action.
+ **Note**: The following examples assume you `.require` the `["^" library/lux/meta/macro/pattern]` module.
+
## Pattern-matching macros in the Standard Library
```clojure
-(case (list 1 2 3)
- (^ (list x y z))
+(when (list 1 2 3)
+ (list x y z)
{.#Some (+ x (* y z))}
_
@@ -29,15 +31,15 @@ Let's see them in action.
You may remember how annoying it was to pattern-match against lists in the [Chapter 5](chapter_5.md) example.
-Well, by using the `^` pattern-matching macro, you can use any normal macros you want inside the pattern to profit from their code-construction capacities.
+Well, `when` will expand any **normal** macros inside your patterns before proceeding to pattern-match against the input data.
```clojure
... Multi-level pattern matching.
... Useful in situations where the result of a branch depends on further refinements on the values being matched.
... For example:
-(case (split (size static) uri)
- (^multi {.#Some [chunk uri']}
- [(text::= static chunk) #1])
+(when (split (size static) uri)
+ (^.multi {.#Some [chunk uri']}
+ [(text::= static chunk) #1])
(match_uri endpoint? parts' uri')
_
@@ -45,16 +47,16 @@ Well, by using the `^` pattern-matching macro, you can use any normal macros you
... Short-cuts can be taken when using boolean tests.
... The example above can be rewritten as...
-(case (split (size static) uri)
- (^multi {.#Some [chunk uri']}
- (text::= static chunk))
+(when (split (size static) uri)
+ (^.multi {.#Some [chunk uri']}
+ (text::= static chunk))
(match_uri endpoint? parts' uri')
_
{.#Left (format "Static part " (%.text static) " doesn't match URI: " uri}))
```
-I **love** `^multi`.
+I **love** `multi`.
It's one of those features you don't need often, but when you do, it saves the day.
@@ -62,24 +64,24 @@ The possibilities are endless when it comes to the refinement you can do, and wh
```clojure
... Allows you to simultaneously bind and de-structure a value.
-(def (hash (^@ set [element_hash _]))
+(def (hash (^.let set [element_hash _]))
(list#mix (function (_ elem acc)
(n.+ (# element_hash hash elem) acc))
0
(set.list set)))
```
-`^@` is for when you want to deal with a value both as a whole and in parts.
+`let` is for when you want to deal with a value both as a whole and in parts.
```clojure
-... Same as the "open" macro, but meant to be used as a pattern-matching macro for generating local bindings.
+... Same as the "use" macro, but meant to be used as a pattern-matching macro for generating local bindings.
... Can optionally take an aliasing text for the generated local bindings.
-(def .public (range (^open "[0]") from to)
+(def .public (range (.open "[0]") from to)
(All (_ a) (-> (Enum a) a a (List a)))
(range' <= succ from to))
```
-`^open` allows you to open structures using local variables during pattern-matching.
+`.open` allows you to open structures using local variables during pattern-matching.
It's excellent when taking structures as function arguments, or when opening structures locally in `let` expressions.
@@ -97,15 +99,15 @@ It's excellent when taking structures as function arguments, or when opening str
(def (weekend? day)
(-> Day Bit)
- (case day
- (^or {#Saturday} {#Sunday})
+ (when day
+ (^.or {#Saturday} {#Sunday})
true
_
false))
```
-`^or` patterns allow you to have multiple patterns with the same branch.
+`or` patterns allow you to have multiple patterns with the same branch.
It's a real time-saver.
@@ -113,11 +115,11 @@ It's a real time-saver.
... It's similar to do-template, but meant to be used during pattern-matching.
(def (beta_reduce env type)
(-> (List Type) Type Type)
- (case type
+ (when type
{.#Primitive name params}
{.#Primitive name (list#map (beta_reduce env) params)}
- (^template [<tag>]
+ (^.with_template [<tag>]
[{<tag> left right}
{<tag> (beta_reduce env left) (beta_reduce env right)}])
([.#Sum]
@@ -125,9 +127,9 @@ It's a real time-saver.
[.#Function]
[.#Apply])
- (^template [<tag>]
+ (^.with_template [<tag>]
[{<tag> old_env def}
- (case old_env
+ (when old_env
{.#End}
{<tag> env def}
@@ -146,9 +148,9 @@ It's a real time-saver.
type))
```
-`^template` is `^or`'s big brother.
+`with_template` is `or`'s big brother.
-Whereas `^or` demands that you provide the exact patterns you want (and with a single branch body), `^template` lets you provide templates for both the patterns and bodies, and then fills the blanks with all the given parameters.
+Whereas `or` demands that you provide the exact patterns you want (and with a single branch body), `with_template` lets you provide templates for both the patterns and bodies, and then fills the blanks with all the given parameters.
You can save yourself quite a lot of typing (and debugging) by reusing a lot of pattern-matching code with this macro.
@@ -157,11 +159,11 @@ It's a great asset!
```clojure
... Allows destructuring of streams in pattern-matching expressions.
... Caveat emptor: Only use it for destructuring, and not for testing values within the streams.
-(let [(^stream& x y z _tail) (some_stream_function 1 2 3)]
+(let [(stream.pattern x y z _tail) (some_stream_function 1 2 3)]
(func x y z))
```
-`^stream&` hails from the `library/lux/data/collection/stream` module, and it's quite special, because it allows you to de-structure something you normally wouldn't be able to: functions.
+`stream.pattern` hails from the `library/lux/data/collection/stream` module, and it's quite special, because it allows you to de-structure something you normally wouldn't be able to: functions.
You see, Lux streams (as defined in `library/lux/data/collection/stream`) are implemented using functions.
@@ -169,7 +171,7 @@ The reason is that they are infinite in scope, and having an infinite data-struc
Well, no biggie!
-`^stream&` does some black magic to make sure you can de-structure your stream just like any other data-structure.
+`stream.pattern` does some black magic to make sure you can de-structure your stream just like any other data-structure.
## How to make your own
@@ -183,13 +185,13 @@ After that, you'll receive all the branches (pattern + body) following the call
You can process all your inputs as you wish, but in the end you must produce an even number of outputs (even, because the outputs must take the form of pattern+body pairs).
-These will be further macro-expanded until all macros have been dealt with and only primitive patterns remain, so `case` can just go ahead and do normal pattern-matching.
+These will be further macro-expanded until all macros have been dealt with and only primitive patterns remain, so `when` can just go ahead and do normal pattern-matching.
You may wonder: _why do I receive the body?_
The answer is simple: depending on your macro, you may be trying to provide some advance functionality that requires altering (or replicating) the code of the body.
-For example, `^or` copies the body for every alternative pattern you give it, and `^template` must both copy and customize the bodies (and patterns) according to the parameters you give it.
+For example, `^.or` copies the body for every alternative pattern you give it, and `^.with_template` must both copy and customize the bodies (and patterns) according to the parameters you give it.
But receiving the next set of branches takes the cake for the weirdest input.
@@ -197,32 +199,30 @@ _What gives?_
It's simple: some macros are so advanced that they require altering not just their bodies, but anything that comes later.
-A great example of that is the `^multi` macro.
+A great example of that is the `^.multi` macro.
-`^multi` performs some large-scale transformations on your code which require getting access to the rest of the code after a given usage of `^multi`.
+`^.multi` performs some large-scale transformations on your code which require getting access to the rest of the code after a given usage of `^.multi`.
However, most of the time, you'll just return the branches (and sometimes the body) unchanged.
-To make things easier to understand, here is the implementation of the `^` macro, from the `library/lux` module:
+To make things easier to understand, here is the implementation of the `or` macro, from the `library/lux/meta/macro/pattern` module:
```clojure
-(macro: (^ tokens)
- (case tokens
- {#Item [_ {#Form {#Item pattern {#End}}}] {#Item body branches}}
- (do meta_monad
- [pattern+ (full_expansion pattern)]
- (case pattern+
- {#Item pattern' {#End}}
- (in (list& pattern' body branches))
-
- _
- (failure "^ can only expand to 1 pattern.")))
-
- _
- (failure "Wrong syntax for ^ macro")))
+(def .public or
+ (pattern
+ (macro (_ tokens)
+ (when tokens
+ (list.partial [_ {.#Form patterns}] body branches)
+ (when patterns
+ {.#End}
+ (///.failure (..wrong_syntax_error (symbol ..or)))
+
+ _
+ (.let [pairs (.|> patterns
+ (list#each (function (_ pattern) (list pattern body)))
+ list#conjoint)]
+ (///#in (list#composite pairs branches))))
+ _
+ (///.failure (..wrong_syntax_error (symbol ..or)))))))
```
-The `^` prefix given to PM-macros was chosen simply to make them stand-out when you see them in code.
-
-There is nothing special attached to that particular character.
-