From f691489bf8b0fea952ac90da7bbd3c7646bd691d Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 25 Feb 2019 21:02:26 -0600 Subject: [PATCH] DOCS: A whole bunch of improvements. * Changed the ordering so that tutorials come first. * Added a tutorial on recursive modules. --- docsite/site/content/_index.md | 17 +- docsite/site/content/reference/_index.md | 2 +- docsite/site/content/reference/converters.md | 2 +- docsite/site/content/stdlib/_index.md | 2 +- .../content/{how-to => tutorials}/_index.md | 9 +- .../content/tutorials/recursive-modules.md | 159 ++++++++++++++++++ .../content/{how-to => tutorials}/script.md | 0 7 files changed, 178 insertions(+), 13 deletions(-) rename docsite/site/content/{how-to => tutorials}/_index.md (56%) create mode 100644 docsite/site/content/tutorials/recursive-modules.md rename docsite/site/content/{how-to => tutorials}/script.md (100%) diff --git a/docsite/site/content/_index.md b/docsite/site/content/_index.md index d574918..b2ef281 100644 --- a/docsite/site/content/_index.md +++ b/docsite/site/content/_index.md @@ -85,23 +85,28 @@ tbody td:nth-child(even) { UCGJSonnetDhall Variablesx xx - + Conditionalsxxx - + Functionsxxx - + Modules/Classesxxx - -Importsxxx +Importsxxx + Std LibMinimalxPrelude -Type SafetyInferred types and schema validationUnknownInferred types + +Type SafetyInferred types and schema validationUnknownInferred types + + +Guaranteed to terminateNoNoYes + diff --git a/docsite/site/content/reference/_index.md b/docsite/site/content/reference/_index.md index c2cc31e..01030c3 100644 --- a/docsite/site/content/reference/_index.md +++ b/docsite/site/content/reference/_index.md @@ -1,7 +1,7 @@ +++ title = "The UCG Language Reference" slug = "reference" -weight = 2 +weight = 3 sort_by = "weight" in_search_index = true +++ diff --git a/docsite/site/content/reference/converters.md b/docsite/site/content/reference/converters.md index c177fdf..ed8f4b2 100644 --- a/docsite/site/content/reference/converters.md +++ b/docsite/site/content/reference/converters.md @@ -1,5 +1,5 @@ +++ -title = "UCG Converters" +title = "Converters" weight = 5 sort_by = "weight" in_search_index = true diff --git a/docsite/site/content/stdlib/_index.md b/docsite/site/content/stdlib/_index.md index 66b9a19..582663c 100644 --- a/docsite/site/content/stdlib/_index.md +++ b/docsite/site/content/stdlib/_index.md @@ -1,7 +1,7 @@ +++ title = "The UCG Standard Library" slug = "stdlib" -weight = 3 +weight = 4 sort_by = "weight" in_search_index = true +++ diff --git a/docsite/site/content/how-to/_index.md b/docsite/site/content/tutorials/_index.md similarity index 56% rename from docsite/site/content/how-to/_index.md rename to docsite/site/content/tutorials/_index.md index b840a9b..4c1e556 100644 --- a/docsite/site/content/how-to/_index.md +++ b/docsite/site/content/tutorials/_index.md @@ -1,12 +1,13 @@ +++ -title = "HowTo Guides" -weight = 4 +title = "Tutorials" +weight = 2 sort_by = "weight" in_search_index = true +++ -A collection of useful HowTo Guides +A collection of useful Tutorials ---------- Some example use cases for UCG. -* Creating a launch script for a docker container. \ No newline at end of file +* Creating a launch script for a docker container +* Recursive Modules \ No newline at end of file diff --git a/docsite/site/content/tutorials/recursive-modules.md b/docsite/site/content/tutorials/recursive-modules.md new file mode 100644 index 0000000..6566e2d --- /dev/null +++ b/docsite/site/content/tutorials/recursive-modules.md @@ -0,0 +1,159 @@ ++++ +title = "Recursive Modules and their Uses" +slug = "recursive-modules" +weight = 2 +in_search_index = true ++++ + +Modules are the only expression in UCG with the capablility to perform +recursion using self import[^1]. This is safe because statements in a module +are evaluated at module copy time. This is useful when you need to traverse a +UCG value deeply. You can see an example of this in the UCG `std/schema.ucg` +module where we perform a deep type comparison between two UCG values. + +As an example we will create a module that can do a dynamic deep index into a +tuple by using recursion. The module will take in a tuple to traverse and a +list of field names to descend into. + +Create a file called `deep_index.ucg` and put the following into it. + +``` +let deep_index = module { + tpl = {}, // We expect that tuples here. + names = [], // We expect a list of names here. +} => { + // todo +}; +``` + +Our `deep_index` module expects a tuple and list of names. We set the defaults +to an empty tuple and an empty list so anything that isn't either those or NULL +will be a compile error when we call our module. Next we need to check for the +existence of the first name in the tuple. + +``` +let deep_index = module { + tpl = {}, // We expect tuples here. + names = [], // We expect a list of names here. +} => (result) { + // Fill in + + let result = select ((mod.names.0) in mod.tpl), NULL, { + true = mod.tpl.(mod.names.0), + }; +}; +``` + +There are a few things to note here. We use the out expression `(result)` to +make this module more ergonomic. We reference our modulr arguments with the +`mod.` prefix. We also surround part of our selector with parens in +`(mod.tpl.(mod.names.0))` to force the last selector to pick the field +dynamically from the result of the grouped expression. + +As written this module has a few problems though. + +* It is unsafe for inputs where the list of names is empty +* it is unsafe for inputs where the list of names contains items that are not + strings. + +## Handling Unsafe inputs. + +We want it to be a compile error if anyone calls our module with inputs of the +wrong shape. We have two possibilities to handle. NULL inputs for either +argument or an list that contains items that are not strings. + +``` +let deep_index = module { + tpl = {}, // We expect tuples here. + names = [], // We expect a list of names here. +} => (result) { + // import some useful items from the std lib + let schema = import "std/schema.ucg"; + let l = import "std/lists.ucg"; + + // Assert some compile time expectations. + // First ensure that names is a list of strings. + (names != NULL && schema.shaped{val=names, shape=[""]}) || + fail "names must be non NULL and a list of strings"; + // Now ensure that tpl is not NULL + tpl != NULL || fail "tpl must not be NULL"; + + let select_field = func(name) select (name in mod.tpl), NULL { + true = mod.tpl.(name), + }; + + let result = select l.len(names) > 0, mod.tpl, { + true = select_field(mod.names.0), + }; +}; +``` + +Our module will now throw appropriate compile time errors if we pass in invalid +input shapes. We also correctly handle the case where we have no more names to +descend into. We now need to handle calling our module recursively. + +## Module Recursion + +In order to do recursion we have to import ourself. + +``` +let deep_index = module { + tpl = {}, // We expect that tuples here. + names = [], // We expect a list of names here. +} => (result) { + // import some useful items from the std lib + let schema = import "std/schema.ucg"; + let l = import "std/lists.ucg"; + + // import ourself now. + let self = import "deep_index.ucg".deep_index; + + // Assert some compile time expectations. + // First ensure that names is a list of strings. + (names != NULL && schema.shaped{val=names, shape=[""]}) || + fail "names must be non NULL and a list of strings"; + // Now ensure that tpl is not NULL + tpl != NULL || fail "tpl must not be NULL"; + + let name = mod.names.0; + let fail_msg = "Attempt to descend into a non existent name @" % (name); + + // Field selector function that throws a compile error if the field + // doesn't exist. + let select_field = func(name) => select (name in mod.tpl), fail fail_message { + true = mod.tpl.(name), + }; + + // calculate our next value. + // If there are no more names then something has gone wrong and + // we should hard fail. + let next = select l.len(names) > 0, mod.tpl, fail "Impossible" { + true = select_field(name), + }; + + // we need to get the tail of our list. + let tail = l.tail(names); + + // if the tail is non empty then recurse down. + // If the tail is empty then return the current value. + let result = select l.len(tail) > 0, next, { + // recurse + true = self{tpl=result, names=tail}, + }; +}; +``` + +Our `deep_index` module can now descend down into a tuple given a list of field +names. If any of the names does not exist it will be a compile failure. + +## Final notes + +One consquence of this method of doing recursion is that you can not reference +the module in the file it is defined in. Doing so would result in a compile +error as UCG detects an import loop. In practice this does not prove to be a +problem since nearly all usages of the module will be outside of the file it is +defined in. + +
+ +[^1]: Technnically functions can do recursion if you manually pass in the same function as an argument to itself, but this is unwieldy and error-prone. \ No newline at end of file diff --git a/docsite/site/content/how-to/script.md b/docsite/site/content/tutorials/script.md similarity index 100% rename from docsite/site/content/how-to/script.md rename to docsite/site/content/tutorials/script.md