From 8196ab379495ab00c11b74b55b6f2fabd99ab351 Mon Sep 17 00:00:00 2001 From: Eduardo Julian Date: Mon, 20 Sep 2021 23:01:35 -0400 Subject: Updates and fixes for the book. --- .../the_lux_programming_language/appendix_c.md | 108 ++++++++++----------- 1 file changed, 50 insertions(+), 58 deletions(-) (limited to 'documentation/book/the_lux_programming_language/appendix_c.md') 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 [] - [( left right) - ( (beta_reduce env left) (beta_reduce env right))]) - ([#.Sum] - [#.Product] - [#.Function] - [#.Apply]) + [{ left right} + { (beta_reduce env left) (beta_reduce env right)}]) + ([.#Sum] + [.#Product] + [.#Function] + [.#Apply]) (^template [] - [( old_env def) + [{ old_env def} (case old_env - #.End - ( env def) + {.#End} + { 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) _ @@ -151,35 +154,24 @@ 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)) _ -- cgit v1.2.3