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.md108
1 files changed, 50 insertions, 58 deletions
diff --git a/documentation/book/the_lux_programming_language/appendix_c.md b/documentation/book/the_lux_programming_language/appendix_c.md
index 85b976c3c..38580fee8 100644
--- a/documentation/book/the_lux_programming_language/appendix_c.md
+++ b/documentation/book/the_lux_programming_language/appendix_c.md
@@ -7,11 +7,13 @@ _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.
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.
But enough chit-chat.
+
Let's see them in action.
## Pattern-matching macros in the Standard Library
@@ -19,10 +21,10 @@ Let's see them in action.
```clojure
(case (list 1 2 3)
(^ (list x y z))
- (#.Some (+ x (* y z)))
+ {.#Some (+ x (* y z))}
_
- #.None)
+ {.#None})
```
You may remember how annoying it was to pattern-match against lists in the [Chapter 5](chapter_5.md) example.
@@ -34,22 +36,22 @@ Well, by using the `^` pattern-matching macro, you can use any normal macros you
... 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) true})
+ (^multi {.#Some [chunk uri']}
+ [(text::= static chunk) #1])
(match_uri endpoint? parts' uri')
_
- (#.Left (format "Static part " (%.text static) " doesn't match URI: " uri)))
+ {.#Left (format "Static part " (%.text static) " doesn't match URI: " uri)})
... 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'])
+ (^multi {.#Some [chunk uri']}
(text::= static chunk))
(match_uri endpoint? parts' uri')
_
- (#.Left (format "Static part " (%.text static) " doesn't match URI: " uri)))
+ {.#Left (format "Static part " (%.text static) " doesn't match URI: " uri}))
```
I **love** `^multi`.
@@ -61,19 +63,19 @@ 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 _]))
- (list::mix (function (_ elem acc)
- (n.+ (\ element_hash hash elem) acc))
- 0
- (set.list set)))
+ (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.
```clojure
... Same as the "open" macro, but meant to be used as a pattern-matching macro for generating local bindings.
-... Can optionally take a "prefix" text for the generated local bindings.
-(def: #export (range (^open ".") from to)
- (All [a] (-> (Enum a) a a (List a)))
+... Can optionally take an aliasing text for the generated local bindings.
+(def: .public (range (^open "[0]") from to)
+ (All (_ a) (-> (Enum a) a a (List a)))
(range' <= succ from to))
```
@@ -83,19 +85,20 @@ It's excellent when taking structures as function arguments, or when opening str
```clojure
... Or-patterns.
-(type: Weekday
- #Monday
- #Tuesday
- #Wednesday
- #Thursday
- #Friday
- #Saturday
- #Sunday)
+(type: .public Day
+ (Variant
+ {#Sunday}
+ {#Monday}
+ {#Tuesday}
+ {#Wednesday}
+ {#Thursday}
+ {#Friday}
+ {#Saturday}))
(def: (weekend? day)
- (-> Weekday Bool)
+ (-> Day Bit)
(case day
- (^or #Saturday #Sunday)
+ (^or {#Saturday} {#Sunday})
true
_
@@ -111,32 +114,32 @@ It's a real time-saver.
(def: (beta_reduce env type)
(-> (List Type) Type Type)
(case type
- (#.Primitive name params)
- (#.Primitive name (list::map (beta_reduce env) params))
+ {.#Primitive name params}
+ {.#Primitive name (list#map (beta_reduce env) params)}
(^template [<tag>]
- [(<tag> left right)
- (<tag> (beta_reduce env left) (beta_reduce env right))])
- ([#.Sum]
- [#.Product]
- [#.Function]
- [#.Apply])
+ [{<tag> left right}
+ {<tag> (beta_reduce env left) (beta_reduce env right)}])
+ ([.#Sum]
+ [.#Product]
+ [.#Function]
+ [.#Apply])
(^template [<tag>]
- [(<tag> old_env def)
+ [{<tag> old_env def}
(case old_env
- #.End
- (<tag> env def)
+ {.#End}
+ {<tag> env def}
_
type)])
- ([#.UnivQ]
- [#.ExQ])
+ ([.#UnivQ]
+ [.#ExQ])
- (#.Parameter idx)
+ {.#Parameter idx}
(maybe.else type (list.item idx env))
- (#.Named name type)
+ {.#Named name type}
(beta_reduce env type)
_
@@ -152,34 +155,23 @@ You can save yourself quite a lot of typing (and debugging) by reusing a lot of
It's a great asset!
```clojure
-... Allows you to extract record members as local variables with the same names.
-... For example:
-(let [(^slots [#foo #bar #baz]) quux]
- (f foo bar baz))
-```
-
-`^slots` is great for working with records, as it allows you to create local variables with the names of the tags you specify.
-
-Now you can work with record member values with ease.
-
-```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 [(^sequence& x y z _tail) (some_sequence_function 1 2 3)]
+(let [(^stream& x y z _tail) (some_stream_function 1 2 3)]
(func x y z))
```
-`^sequence&` hails from the `library/lux/data/collection/sequence` module, and it's quite special, because it allows you to de-structure something you normally wouldn't be able to: functions.
+`^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.
-You see, Lux sequences (as defined in `library/lux/data/collection/sequence`) are implemented using functions.
+You see, Lux streams (as defined in `library/lux/data/collection/stream`) are implemented using functions.
The reason is that they are infinite in scope, and having an infinite data-structure would be... well... impossible (_unless you manage to steal a computer with infinite RAM from space aliens_).
Well, no biggie!
-`^sequence&` does some black magic to make sure you can de-structure your stream just like any other data-structure.
+`^stream&` does some black magic to make sure you can de-structure your stream just like any other data-structure.
-## How to Make your Own
+## How to make your own
The technique is very simple.
@@ -205,7 +197,7 @@ _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 (which is actually the reason those inputs are given to pattern-matching macros in the first place).
+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`.
@@ -216,11 +208,11 @@ To make things easier to understand, here is the implementation of the `^` macro
```clojure
(macro: (^ tokens)
(case tokens
- (#Item [_ (#Form (#Item pattern #End))] (#Item body branches))
+ {#Item [_ {#Form {#Item pattern {#End}}}] {#Item body branches}}
(do meta_monad
[pattern+ (full_expansion pattern)]
(case pattern+
- (#Item pattern' #End)
+ {#Item pattern' {#End}}
(in (list& pattern' body branches))
_