├── LICENSE ├── README.md ├── cloud-database.md ├── clp-pl.md ├── code-transformation.md ├── command-line-options.md ├── currency.md ├── data-structures.md ├── determinism-assertions.md ├── diet.md ├── examples.md ├── fsm.md ├── infer-io.md ├── infer-types.md ├── jquery.md ├── lint.md ├── maps.md ├── mercury-compatibility.md ├── mmap.md ├── mode-indicators.md ├── mode-line.md ├── mode-rewrite.md ├── os-detection.md ├── partial-evaluation.md ├── patches.md ├── protobuf.md ├── semver.md ├── smartmatch.md ├── state-variable.md ├── summary_stats.md ├── switch.md ├── template.md ├── text.md ├── uri.md ├── web-framework.md ├── whitespace-syntax.md └── yaml.md /LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | prolog_library_ideas 2 | ==================== 3 | 4 | Random ideas about libraries for Prolog 5 | 6 | See each `.md` file for details. Once an idea has been implemented, I delete the file because it's visible on my [list of packs](http://www.swi-prolog.org/pack/list?author=michael@ndrix.org&sort=downloads). 7 | 8 | There are also many good ideas and implementations at [The public-domain Prolog library](http://www.j-paine.org/prolog/library.html). Many of those would be excellent to release as packs or build on for packs (with permission, of course). 9 | -------------------------------------------------------------------------------- /cloud-database.md: -------------------------------------------------------------------------------- 1 | # Cloud Storage for Terms 2 | 3 | I want to be able to store 1st-order terms in the cloud (massive scale, shared across apps) as if I were storing them in a local Prolog module. For example, module `foo` is defined somewhere as a cloud module. Then 4 | 5 | ```prolog 6 | foo:assert(things(that,matter,to,michael)), 7 | foo:assert(things(that,matter,to,kinsey)), 8 | foo:things(that,matter,to,Whom). 9 | 10 | % Whom = michael ; 11 | % Whom = kinsey . 12 | ``` 13 | 14 | Behind the scenes, each assert stores all necessary [path indexes](https://drive.google.com/file/d/0B25G2sSG-_Mvd2dETlp2VjNyTGM/view?usp=sharing) for the term in [datastore](https://developers.google.com/appengine/docs/go/datastore/reference). The term is an entity. Each path is stored in a list property on that entity. For a query, we can use [datastore's merge join](http://youtu.be/AgaL6NGpkB8?t=23m) to efficiently calculate the set intersections across the necessary path indexes. There may be a way to have it handle set unions too. Otherwise, we could do it in memory. 15 | 16 | # UI 17 | 18 | The key idea is that one should interact with the cloud data as if it were stored locally in a normal Prolog module. Caching is handled transparently (locally and in memcache). When defining a module that operates this way, there's probably a predicate which defines the cache TTL given a term in that module. 19 | 20 | # API 21 | 22 | The cloud term storage should have a very simple API that's just a partially instantiated term like `foo(_,A,hi)` and responds with JSON like `[{"A":"bar"},{"A":"baz"}]`. That way it can be used from languages other than Prolog. 23 | -------------------------------------------------------------------------------- /clp-pl.md: -------------------------------------------------------------------------------- 1 | # clp(pl) 2 | 3 | A constraint logic programming library in which the constraints are arbitrary Prolog goals. The library accumulates those goals in attributed variables. It uses partial evaluation to simplify and specialize the constraints based on the current evaluation. Constraint goals are reordered or executed to assist in the simplification. Unifications are propagated through the constraints as they become available. Labeling just executes the accumulated, simplified goal to generate solutions. 4 | 5 | 6 | ## Playground 7 | 8 | A place to play around with some examples 9 | 10 | ### Small list 11 | 12 | For example, this code runs out of stack before finding the two valid answers. 13 | 14 | ```prolog 15 | foo(L) :- 16 | length(L,N), 17 | N < 4, 18 | nth1(2,L,b). 19 | ?- findall(L,foo(L),Ls). 20 | ``` 21 | 22 | If `foo/1` is called with an unbound variable, we might make the following observations: 23 | 24 | * because `length/2` produces nonneg `N`, add goal `N >= 0` after `length/2` call 25 | * `N >= 0` and `N < 4` goals combine to `between(0,3,N)` 26 | * notice that `length/2` with two variables produces infinite solutions but `between(0,3,N)` produces only 4, move the `between/3` goal upwards (more deterministic goals should be as early as possible) 27 | * because `nth1(2,L,b)` produces a single solution, move it to the very top 28 | 29 | So it's as if the code were originally: 30 | 31 | ```prolog 32 | foo(L) :- 33 | nth1(2,L,b), 34 | between(0,3,N), 35 | length(L,N). 36 | ``` 37 | 38 | If we called `foo([A,B])` we'd make different observations: 39 | 40 | * propagate the binding for `L` through the goals 41 | * realize that `length([A,B],N)` can be executed to become `N=2` 42 | * realize that `N=2` is a unification and can be propagated through the goals 43 | * realize that `2<4` can be replaced with `true` which can be removed 44 | * realize that `nth1(2,[A,B],b)` can be executed to become `B=b` 45 | 46 | So it's as if the code were originally: 47 | 48 | ```prolog 49 | foo([_,b]). 50 | ``` 51 | 52 | In each case, we get the correct answer and I was able to write the Prolog code without regard to the order in which each goal (constraint) was executed. 53 | 54 | #### Linear logic 55 | 56 | I noticed an interesting use for linear logic in the simplifications above. Assume that the body of a clause is a series of conjunctions of pure (no side effects) goals. Then each goal can be viewed as a resource. We have the following resources (assuming variables share across resources): 57 | 58 | ```prolog 59 | length(L,N). 60 | N < 4. 61 | nth1(2,L,b). 62 | ``` 63 | 64 | Then the simplifications in the first example can be written as linear logic rules roughly like this: 65 | 66 | ```prolog 67 | length(_,N) -o length(_,N) * N >= 0. 68 | N>=L * N0} -o {call(nth1(N,L,E))} * L=[...]. 77 | ``` 78 | -------------------------------------------------------------------------------- /code-transformation.md: -------------------------------------------------------------------------------- 1 | # Code Beautifier and Transformer 2 | 3 | Similar to `Perl::Tidy` or `gofmt`, accept some Prolog code on standard input and print beautified Prolog in a consistent format to standard output. It should apply popular formatting conventions, indenting conventions and whitespace conventions. Tools like this are helpful on large projects to avoid conflict among developers over formatting style. It also makes automated code transformation tools more effective (automated changes aren’t drowning in code formatting changes). 4 | 5 | An automated code transformation tool could be a natural component of this (see `gofmt -r`). It would essentially be a `term_expansion/2` or `goal_expansion/2` macro expander than operates on files when demanded instead of on code during every compile. 6 | -------------------------------------------------------------------------------- /command-line-options.md: -------------------------------------------------------------------------------- 1 | # Command line option parsing 2 | 3 | SWI-Prolog has at least two ways to process command line options: 4 | 5 | * [library(optparse)](http://www.swi-prolog.org/pldoc/doc/swi/library/optparse.pl) 6 | * [argv_options/3](http://www.swi-prolog.org/pldoc/doc_for?object=http_unix_daemon%3Aargv_options/3) 7 | 8 | The first library has the user define a schema for the command line options and also generates automated documentation. The second library just converts `--name=value` pairs into `name(value)` terms. Both libraries make the parsed options available as a list. Lists are convenient, but difficult to query and reason with. 9 | 10 | My ideal command-line parsing library would have the following features: 11 | 12 | * convert command-line arguments into predicates in an `argv` module (or whatever module the user prefers) 13 | * allow, but not require, an argument schema 14 | * work with an arbitrary list of atoms (not just the program's argv) 15 | 16 | For example, a command line like this 17 | 18 | ``` 19 | -v --size=27 --follow-redirects 20 | ``` 21 | 22 | might create a module like this 23 | 24 | ```prolog 25 | :- module(argv, []). 26 | 27 | v. 28 | 29 | size(27). 30 | 31 | follow_redirects. 32 | ``` 33 | 34 | which allows one to write code like: 35 | 36 | ```prolog 37 | main :- 38 | warn("starting program"), 39 | ( argv:size(Size), Size<99 -> true; Size=42 ), 40 | format("You asked for size=%d", [Size]). 41 | 42 | warn(String) :- 43 | ( argv:v -> writeln(String); true ). 44 | ``` 45 | -------------------------------------------------------------------------------- /currency.md: -------------------------------------------------------------------------------- 1 | # Currency 2 | 3 | Make [library(currency)](https://gist.github.com/mndrix/2224422adc4913e715809245a6b02b3a) (from PriceCharting) available as open source. Teach it how to support different currencies based on their symbol (leading or trailing), separator (comma or full stop), and the number of decimals (Dollars, Yen and Bitcoin differ). Perhaps these currency details should be configured via a hook. So you'd end up with something like this: 4 | 5 | ```prolog 6 | % define(Iso4217, Symbol, SymbolPosition, Thousands, Fraction, FractionSize) 7 | :- multifile currency:define/6. 8 | currency:define(usd, '$', leading, ',', '.', 2). 9 | currency:define(jpy, '¥', leading, ',', '', 0). 10 | currency:define(btc, 'BTC', trailing, ',', '.', 4). 11 | 12 | ?- atom_currency('$12.34', U). 13 | U = usd(1234). 14 | 15 | ?- atom_currency(Y, jpy(1234)). 16 | Y = '¥1234'. 17 | 18 | ?- atom_currency(B, btc(1234)). 19 | B = '0.1234 BTC'. 20 | ``` 21 | 22 | There should be variants for other text types: `string_currency/2` and `codes_currency/2`. There should also be a DCG equivalent: `dcg_currency//1`. 23 | 24 | Should we also specify a pip size? That might be helpful for currency traders. 25 | -------------------------------------------------------------------------------- /data-structures.md: -------------------------------------------------------------------------------- 1 | # Logical Data Structures 2 | 3 | Create a library with various data structures having nice, logical implementations. Data structures worth including might be: 4 | 5 | * queue 6 | * stack 7 | * dequeue 8 | * priority queue 9 | * set 10 | * map (probably as a tree or HAMT) 11 | 12 | As with my [map](maps.md) idea, these should be defined in terms of generic interfaces on top of multiple (if necessary) implementations. 13 | 14 | See _The Craft of Prolog_ by O’Keefe for an example of a queue. The biggest trick for each structure is finding a nice, logical, reversible interface describing the true relation between the data structure, its inputs and its outputs. 15 | -------------------------------------------------------------------------------- /determinism-assertions.md: -------------------------------------------------------------------------------- 1 | # Determinism Calling Assertions 2 | 3 | ## By the Caller 4 | 5 | When calling a predicate, one might make certain assumptions about its determinism. Sometimes I’d like to declare those assumptions to help future readers and to get runtime errors if the assumption proves wrong. For example, in the following code `husband_wife(+A,-B) is det`. 6 | 7 | ```prolog 8 | husband_wife(bob, jane). 9 | husband_wife(tom, sue). 10 | ``` 11 | 12 | A caller working with that assumption might write one of the following (alternative syntaxes): 13 | 14 | ```prolog 15 | call_det(husband_wife(bob,Wife)). 16 | det husband_wife(bob, Wife). 17 | husband_wife(bob, Wife) is det. 18 | ``` 19 | 20 | In each case, he declares his determinism expectation. If the call fails or succeeds with a choicepoint, a helpful error is thrown during development. Mercury’s determinism system is a good model to follow. 21 | 22 | An implementation of `call_semidet/1` from the SWI-Prolog mailing list is: 23 | 24 | ```prolog 25 | call_semidet(Goal) :- 26 | call_cleanup(Goal, Det=true), 27 | ( Det == true -> 28 | true 29 | ; % left choicepoints -> 30 | throw(error(mode_error(det,Goal),_)) 31 | ). 32 | ``` 33 | 34 | ## By the Developer 35 | 36 | When developing a predicate, I might like to declare a predicate’s determinism and have a tool verify that my declaration is accurate. It probably makes sense to extract determinism declarations from pldoc comments since the data is already there in a machine-readable format. For example, the tool should fail this code because `husband_wife(bob,Wife)` has multiple answers: 37 | 38 | ```prolog 39 | %% husband_wife(+Husband, -Wife) is det 40 | husband_wife(bob, tina). 41 | husband_wife(bob, sue). 42 | husband_wife(tom, pam). 43 | ``` 44 | 45 | Using Determinism Calling Assertions (above), this could be implemented as a macro expanding to 46 | 47 | ```prolog 48 | husband_wife(A, B) :- 49 | ground(A), 50 | var(B), 51 | !, 52 | call_det(husband_wife_aux(A,B)). 53 | husband_wife_aux(bob, tina). 54 | husband_wife_aux(bob, sue). 55 | husband_wife_aux(tom, pam). 56 | ``` 57 | -------------------------------------------------------------------------------- /diet.md: -------------------------------------------------------------------------------- 1 | # "Functional Pearls: Diets for Fat Sets" by Martin Ewig 2 | 3 | 4 | This would make a great Prolog library for any data type that can define a relation like `succ/2`. Integers are the most obvious application. Union and intersection can be implemented using the obvious insert, size and traversal predicates. 5 | 6 | -------------------------------------------------------------------------------- /examples.md: -------------------------------------------------------------------------------- 1 | # Example-driven Development 2 | 3 | Prolog is well-suited to what I think of as example-driven development. A predicate represents a true relation among some variables. By giving examples and counter examples, we get: 4 | 5 | * documentation 6 | * simple automated tests 7 | * simplistic type inference 8 | * simplistic mode inference 9 | * steadfastness tests 10 | 11 | ## Examples 12 | 13 | Let's consider an implementation for `length/2`: 14 | 15 | ```prolog 16 | length(L,N) :- 17 | length(L,0,N). 18 | 19 | length([],N,N). 20 | length([_|L],N0,N) :- 21 | succ(N0,N1), 22 | length(L,N1,N). 23 | ``` 24 | 25 | We might declare examples like this: 26 | 27 | ```prolog 28 | :- examples 29 | length([],0), 30 | length([a,b],2), 31 | length([_],1). 32 | :- counter_examples 33 | length([1,2,3],7). 34 | ``` 35 | 36 | ## Documentation 37 | 38 | By displaying the examples in HTML documentation, users get a good idea how the predicate should be used. A few accurate, well chosen examples are often more effective at communicating a predicate's purpose than paragraphs of natural language text. Programmers are pretty good at exptrapolating a few examples into a mental model. 39 | 40 | ## Automated Tests 41 | 42 | Each example can be executed as a goal. If it succeeds, consider it a passing test. Counter examples, when executed as goals, should fail. This gives us assurance that our example-based documentation never deviates from the predicate's true behavior. 43 | 44 | ## Simplistic Type Inference 45 | 46 | The predicate arguments shown in the examples allow us to perform primitive type inference. In the examples above, the first argument has values `[]`, `[a,b]`, `[_]` and `[1,2,3]`. By executing code like this, we can infer possible types: 47 | 48 | ```prolog 49 | ?- dif(T,impossible), 50 | foreach( member(V,[[],[_],[a,b],[1,2,3]]) 51 | , error:has_type(T,V) 52 | ). 53 | T = any ; 54 | T = acyclic ; 55 | T = nonvar ; 56 | T = proper_list ; 57 | T = list ; 58 | T = list_or_partial_list ... 59 | ``` 60 | 61 | We can choose among the possible types with various heuristics. Alternatively, the most precise type among the options is `proper_list`. We can calculate "most precise" with something roughly like this: 62 | 63 | ```prolog 64 | more_precise_than(T1, T2) :- 65 | forall( between(1,1000,_) 66 | , ( quickcheck:arbitrary(T1,Val) 67 | , error:has_type(T2,Val) 68 | ) 69 | ), 70 | asserta(more_precise_than(T1,T2) :- !). 71 | ``` 72 | 73 | `more_precise_than/2` gives us a partial order. By using a [topological sort](http://en.wikipedia.org/wiki/Topological_sorting) we can pick some types as the most precise. A [handy video demo](http://www.showme.com/sh/?h=qovixPc). 74 | 75 | ### type_value/2 76 | 77 | The predicates `error:has_type/2` and `quickcheck:arbitrary/2` suggest a more general relation: `type_value/2`. It would have the following modes: 78 | 79 | ```prolog 80 | type_value(+Type,+Value) is semidet. 81 | type_value(+Type,-Value) is det. % at random 82 | type_value(-Type,+Value) is multi. 83 | ``` 84 | 85 | ## Simplistic Mode Inference 86 | 87 | A predicate's modes communicate three related details about argument instantiation and a predicate's solutions: 88 | 89 | * which arguments must start as `nonvar` 90 | * which initially `var` arguments will finish as `nonvar` 91 | * how many solutions are likely 92 | 93 | By executing each example in every possible combination of var/nonvar arguments, we get a table like this: 94 | 95 | ``` 96 | length([],0) 1 step, 0 extra : ++ is det 97 | length(_,_) 1 step, n extra : -- is multi 98 | length([],_) 1 step, 0 extra : +- is det 99 | length(_,0) 1 step, 0 extra : -+ is det. 100 | 101 | length([a,b],2) 1 step, 0 extra : ++ is det 102 | length(_,_) 3 step, n extra : -- is multi 103 | length([a,b],_) 1 step, 0 extra : +- is det 104 | length(_,2) 1 step, 0 extra : -+ is det. 105 | 106 | length([_],1) 1 step, 0 extra : ++ is det 107 | length(_,_) 2 step, n extra : -- is multi 108 | length([_],_) 1 step, 0 extra : +- is det 109 | length(_,1) 1 step, 0 extra : -+ is det. 110 | 111 | % counter example 112 | length([1,2,3],7) 1 step, 0 extra : ++ is semidet 113 | length(_,_) 7 step, n extra : -- is multi 114 | length([1,2,3],_) 1 step, 0 extra : +- is det 115 | length(_,7) 1 step, 0 extra : -+ is det. 116 | ``` 117 | 118 | For a predicate of arity N, there are 2^N possible modes. Each mode for which all examples and counterexamples behave as expected is a legitimate mode for this predicate. For each mode, choose the determinism with the most solutions. For length/2, that leads to the following inferred modes: 119 | 120 | ```prolog 121 | length(+,+) is semidet. 122 | length(+,-) is det. 123 | length(-,+) is det. 124 | length(-,-) is multi. 125 | ``` 126 | 127 | If one of these modes is incorrect, the user can provide an example or counter example to show the discrepancy. The inference algorithm can then come to the right conclusion. 128 | 129 | ## Steadfastness Tests 130 | 131 | ["Steadfastness basically means that you cannot force a predicate down the wrong path by filling in output arguments wrongly"](http://permalink.gmane.com/gmane.comp.lang.ai.prolog.swi/9785). Based on the inferred modes, fill in some output arguments "wrongly" (by giving them a `nonvar` value) and make sure the example still works. 132 | -------------------------------------------------------------------------------- /fsm.md: -------------------------------------------------------------------------------- 1 | # Finite State Machines 2 | 3 | Make a small library for describing and working with finite state machines. I want as little syntactic overhead as possible. States are just atoms. Transitions are just atoms. Perhaps it looks something like this, with a single predicate defining a machine. 4 | 5 | ```prolog 6 | %% light_switch(+Event, +CurrentState -NextState) is det. 7 | % 8 | % Define a finite state machine named "light_switch". This predicate defines 9 | % the states, the events and the transitions. 10 | light_switch(flip, on, off). 11 | light_switch(flip, off, on). 12 | ``` 13 | -------------------------------------------------------------------------------- /infer-io.md: -------------------------------------------------------------------------------- 1 | # Infer IO Usage 2 | 3 | Infer whether a predicate does IO (prints to screen, opens a file, sleeps, etc). All foreign predicates are considered to do IO (except for a whitelisted group of exceptions). This whitelist must be hardcoded as facts in the library. For other predicates, they perform IO if one of their subgoals performs IO. This should be an easy inference rule to implement recursively. 4 | 5 | This can be useful for static analysis. It could also be useful for running untrusted Prolog code. It’s not perfect since one can build atoms from arbitrary data and then execute those atoms. To be truly safe, the underlying Prolog engine must provide something like `call_without_io(...)` but this inference could cover many cases. 6 | 7 | `library(sandbox)` does something similar. Investigate there before writing any code. 8 | -------------------------------------------------------------------------------- /infer-types.md: -------------------------------------------------------------------------------- 1 | # Type Inference 2 | 3 | It can be helpful for documentation and debugging and compile-time error detection to be able to infer the types of variables in goals. Prolog has a great system for describing types based on `error:has_type/2`. This predicate simply recognizes values which belong to the type in question. Types are defined as the conjunction and disjunction of Prolog goals (a boolean algebra) and boolean algebra is a distributive lattice. That means we can learn types and join them with other learned types to obtain a supertype. 4 | 5 | This library has a predicate something like `infer_types(+Goal, -Types:list(pair))`. Running the predicate on a goal gives you a list of variables and their associated types. The types can be named (like `atom`) or compound (like `integer(N), 0 =:= N mod 2`). For example, 6 | 7 | ```prolog 8 | atom_length(Atom, Length) :- 9 | atom_codes(Atom, Codes), 10 | length(Codes, Length). 11 | ?- infer_types(atom_length(A,N), Ts). 12 | Ts = [A=atom, N=integer]. 13 | ``` 14 | 15 | Calculating the type of a conjunction is calculating the types of variables in the first goal and the types of variables in the second goal. Then join those two type inferences to produce the type inference of the conjunction. 16 | 17 | We could start with simple boolean algebra join rules (assume `v` is join) such that 18 | 19 | ```prolog 20 | nonvar(X) v atom(X) 21 | ``` 22 | 23 | is 24 | 25 | ``` 26 | nonvar(X), 27 | atom(X). 28 | ``` 29 | 30 | but later specialize the join rules so that it’s just `atom(X)`. That’s because an atom is a more specialized case of nonvar. 31 | -------------------------------------------------------------------------------- /jquery.md: -------------------------------------------------------------------------------- 1 | # jQuery-like API 2 | 3 | The [jQuery](http://api.jquery.com/) library makes it easy to search and modify HTML documents. The API has been [ported to Go](https://github.com/PuerkitoBio/goquery) where it's proven equally helpful. It could be helpful to have the API available in Prolog too. 4 | 5 | ## Start with CSS matching 6 | 7 | A good place to start would be to implement a CSS selector library. That's the foundation of all good jQuery knock offs. 8 | -------------------------------------------------------------------------------- /lint.md: -------------------------------------------------------------------------------- 1 | # Lint Tool 2 | 3 | Make a lint-like tool for spotting suspicious Prolog constructs. See examples section below. 4 | 5 | According to [this thread](http://computer-programming-forum.com/55-prolog/514e80369d8e92ea.htm), a tool called nit is available as part of [NU-Prolog](http://ww2.cs.mu.oz.au/~lee/src/nuprolog/). Perhaps it could be ported to SWI-Prolog or ideas taken from it. 6 | 7 | There’s an existing SWI-Prolog linting tool called [BLint](http://www.fing.edu.uy/~gbrown/prolog/blint.html). It could probably be packaged up as a pack and made usable. 8 | 9 | This could probably be implemented via `library(prolog_codewalk)`. By loading the target code into memory, walking every clause, reporting on suspicious patterns. 10 | 11 | I’d love to integrate this tool into SWI-Prolog’s `make/0` command. It already does undefined predicate checks and runs tests. It’d be a helpful place to report on other suspicious constructs. 12 | 13 | In an unrelated email thread, Richard A. O’Keefe said, 14 | 15 | > The thing I dream of for Prolog is something like PLT Scheme (now Racket) or clang --analyze, something that helps me write *working* code. 16 | 17 | That really is the emphasis of this lint tool. Help developers get from where they are to working code, without having to write and run a bunch of tests. 18 | 19 | Erlang’s success typing is another great place to look. It only warns of problems when it can prove that you did something wrong. If it can’t prove it, it stays silent. 20 | 21 | It’s worth considering whether the lint tool should work at the syntactic level or at the code level (aka “post-macro” level). SWI-Prolog’s semantic singletons already do some good analysis at the code or compiler level. Maybe there’s space for a syntactic linter. We could allow library authors to define additional lint rules based on correct usage for their library. For example, it’s typically an error to call `delay/1` before `!/0` in a clause. The linter shouldn’t have to hard-code that rule, but because of macro expansion it’s hard to detect it after macros have been expanded. Performing checks at the syntactic level makes both aspects easier, I think. 22 | 23 | ## Examples 24 | 25 | Some small examples: 26 | 27 | * mismatch between `format/2` template and number of arguments 28 | * variables in a term with suspiciously similar names (sans trailing numbers) 29 | * report on undefined predicates 30 | * ! in multifile predicates 31 | 32 | ### `forall/2` loop variables 33 | 34 | The first argument of `forall/2` must have at least one variable that's local to the forall/3. For example, this is almost certainly mistake eventhough the goal succeeds: 35 | 36 | ```prolog 37 | N = 3, 38 | forall( between(1,7,N), writeln(N) ) 39 | ``` 40 | 41 | The developer probably intended: 42 | 43 | ```prolog 44 | N = 3, 45 | forall( between(1,7,I), writeln(I) ) 46 | ``` 47 | 48 | ### `bagof/3` and ^ variables 49 | 50 | #### Free variables must exist inside 51 | 52 | If `bagof/3` or `setof/3` has a second argument describing free variables (with `^/2`) each of those free variables must appear somewhere inside the goal: 53 | 54 | ```prolog 55 | X = hi, 56 | bagof(Y, X^foo(Z,Y), Ys) 57 | ``` 58 | 59 | The developer probably intended: 60 | 61 | ```prolog 62 | X = hi, 63 | bagof(Y,Z^foo(Z,Y), Ys) 64 | ``` 65 | 66 | #### Non-escaping variables must be declared 67 | 68 | If `bagof/3` or `setof/3` have variables that are used in the second argument, not mentioned in the first argument and not mentioned outside the goal, those variables must be declared as free variables using `^/2`: 69 | 70 | ```prolog 71 | Start = 1, 72 | End = 7, 73 | bagof(Ls,(mature_loans_by_day_cache(Dt,Ls),Start= writeln(elway), 7 | 42 -> writeln(answer), 8 | (alpha; beta) -> writeln(greek_letter), 9 | between(1,6) => writeln(bar) 10 | ]. 11 | ``` 12 | 13 | becomes 14 | 15 | ```prolog 16 | compiled_switch_1234(7) :- 17 | !, 18 | writeln(elway). 19 | compiled_switch_1234(42) :- 20 | !, 21 | writeln(answer). 22 | compiled_switch_1234(A) :- 23 | ( (A=alpha;A=beta) -> writeln(greek_letter) 24 | ; between(1,6,A) -> writeln(bar) 25 | ; fail 26 | ). 27 | 28 | ..., 29 | compiled_switch_1234(X). 30 | ``` 31 | 32 | Consider desugaring to something that uses the [smartmatch operator](smartmatch.md) so that one can match against patterns supplied by libraries. Also consider [Python’s switch proposal](http://www.python.org/dev/peps/pep-3103/) for ideas. 33 | 34 | Ideally, switch cases where we’re just doing unification would be optimized by automatically creating an intermediate predicate. That gives us really good performance by leveraging Prolog’s first term index. 35 | 36 | If cuts inside the switch statement are broken, generate a compile time error saying so. That way it doesn’t surprise users. 37 | -------------------------------------------------------------------------------- /template.md: -------------------------------------------------------------------------------- 1 | # Templating library 2 | 3 | I want a Prolog templating library with the following features: 4 | 5 | * compile templates from quasiquotes into a DCG 6 | * loops over lists or backtracked solutions 7 | * helpful defaults to handle common text generation use cases 8 | 9 | Most templating libraries think of a template as a way to generate text. I believe it's more helpful to think of a template as a way to generate code which generates text. In Prolog, code that generates text is often done the most naturally with DCGs. So a Prolog template should be a way to easily generate DCGs. 10 | 11 | For example, this quasiquoted template 12 | 13 | ```text 14 | {|template(hello)||Hello {{name}}.|} 15 | ``` 16 | 17 | might generate code like 18 | 19 | ```prolog 20 | hello(X) --> 21 | "Hello ", 22 | template:value(X, name), 23 | ".". 24 | 25 | % library code 26 | template:value(X,Field) --> 27 | { is_dict(X), get_dict(Field,X,Y) }, 28 | template:textual(Y), 29 | !. 30 | template:value(X,Field) --> 31 | call(Field,X), 32 | !. 33 | template:value(X,Field) --> 34 | { call(Field,X,Y) }, 35 | template:textual(Y), 36 | !. 37 | 38 | % template:textual//1 is a multifile predicate defining a 39 | % textual representation for atoms, numbers, strings, etc. 40 | ``` 41 | 42 | ## Syntax 43 | 44 | One option would be to adopt the syntax and keywords of an existing template language like [mustache](https://mustache.github.io/mustache.5.html) or [Go's template](https://golang.org/pkg/text/template/). Another option would be to make a template library that's designed from the ground up with Prolog in mind. For the first templating package, it might be best to borrow syntax and semantics from an existing package. That would keep me focused on quasiquote and DCG implementation rather than deciding which features are useful for a templating system. 45 | -------------------------------------------------------------------------------- /text.md: -------------------------------------------------------------------------------- 1 | # Generic string library 2 | 3 | I want a generic, consistent interface to string data. The library should provide an interface across all SWI-Prolog's different string representations: 4 | 5 | * atoms 6 | * lists of codes 7 | * strings 8 | 9 | Start by implementing the interface for these representations. Then implement it for other text data structures. 10 | 11 | Or maybe I just standardize on SWI-Prolog's built in string type and build everything else around that. In that case, this library becomes a massive collection of useful string predicates. 12 | 13 | ## Predicates 14 | 15 | The library should have the following predicates (and many more): 16 | 17 | * `is_text(+Text) is semidet` - true if Text a valid text representation 18 | * `text_atom(Text:text,Atom:atom)` 19 | * `text_codes(Text:text,Codes:list(nonneg))` 20 | * `text_string(Text:text,String:string)` 21 | * `text_length(+Text:text, -Length:nonneg)` 22 | * see [Haskell's Data.Text](http://hackage.haskell.org/package/text-1.2.2.1/docs/Data-Text.html) for ideas 23 | * see [Go's strings](https://golang.org/pkg/strings/) for predicate ideas 24 | * ... 25 | 26 | 27 | ## Typeclasses, interfaces, multifile predicates, oh my! 28 | 29 | The predicates defined in this library are all `multifile` so that third-party libraries can add their own string-like data structures. Many of these predicates can have default implementations that are implemented in terms of others. For example, we might have something hypothetical like: 30 | 31 | ```prolog 32 | text_length(Text,Length) :- 33 | text_codes(Text,Codes), 34 | length(Codes,Length). 35 | ``` 36 | 37 | An implementation of this interface should be able to implement `text_length/2` directly or implement the dependencies and rely on the default implementation. This is similar to the way that Haskell typeclasses work and it's really handy. 38 | 39 | Prolog doesn't have a well-defined convention for typeclass-like constructs. The closest thing is `multifile` predicates, but that requires each implementation to be aware of dangling choicepoints, etc. I'd like something more declarative: 40 | 41 | * a typeclass can define which predicates are part of its interface 42 | * a module can declare 43 | * that a specific value meets that interface 44 | * which predicates on that value implement which interface methods 45 | * the typeclass module chooses an implementation based on these declarations and the default implementations 46 | 47 | 48 | ## String representations 49 | 50 | Fill this section with data structures that would make good string implementations in Prolog. [Ropes](https://en.wikipedia.org/wiki/Rope_(data_structure)) come to mind, but I don't know much about them. 51 | -------------------------------------------------------------------------------- /uri.md: -------------------------------------------------------------------------------- 1 | # URI library 2 | 3 | SWI Prolog comes with [library(uri)](http://www.swi-prolog.org/pldoc/doc/swi/library/uri.pl). It has all the right pieces for a URI library, but has some weaknesses: 4 | 5 | * Pieces missing from a URI are represented as unbound variables. This confounds the idea of "unknown" with "known to be absent". 6 | * Too much code is needed for common relations on URIs (accessing the hostname of an HTTP URI, etc). 7 | * No support for scheme-specific relations. For example, `tag` and `data` URIs have specialized operations. 8 | * No support for adding support for more URI schemes as they come along 9 | * Requires a separate library for URI quasiquotations 10 | * Uses atoms rather than strings internally (minor) 11 | 12 | I'd like a single, high level URI library that addresses all these concerns. 13 | 14 | 15 | # Possible library names 16 | 17 | The obvious name, `uri`, is already taken. Perhaps [urial](https://en.wikipedia.org/wiki/Urial) or [uriel](https://en.wikipedia.org/wiki/Uriel) would be appropriate. 18 | -------------------------------------------------------------------------------- /web-framework.md: -------------------------------------------------------------------------------- 1 | # Prolog Web Framework 2 | 3 | Writing a web server in SWI Prolog requires a lot of repetitive, low level coding. I'd like something that's more declarative. 4 | 5 | ## Use Cases 6 | 7 | A collection of use cases: 8 | 9 | * dispatch requests to predicates based on unification 10 | * based on request method (`get`, `post`, `head`, etc). 11 | * based on path (`foo/Bar/baz`, notice the `Bar` variable) 12 | * easy access to request details (path, parameters, form, url, etc) 13 | * unambiguous access to request details (request as list creates ambiguity between request components) 14 | * support middleware to factor out common layers 15 | * request logging 16 | * proxy header handling 17 | * memory limits 18 | * request time limits 19 | * converting request body into a term based on `Content-Type` header 20 | * converting request parameters into terms based on the parameter name 21 | * built on a generic web server interface like 22 | * [PEP33](http://www.python.org/dev/peps/pep-0333) 23 | * [Rack](http://rack.rubyforge.org/doc/SPEC.html) 24 | * [JSGI](http://jackjs.org/jsgi-spec.html) 25 | * [PSGI](http://search.cpan.org/~miyagawa/PSGI/PSGI.pod) 26 | 27 | ## HTTP Request library 28 | 29 | At the base of all these ideas is a library for parsing and representing HTTP requests. By providing a single, common representation of an HTTP request, web servers and middleware have a lingua franca allowing them to interoperate. 30 | 31 | ### Atoms or Strings? 32 | 33 | One must choose whether to represent textual content as an atom or as a string. We use an atom if the textual content is drawn from a "small" set of possible values. Otherwise, we use a string. 34 | 35 | So HTTP methods are atoms (there are only a few of them). HTTP paths components are atoms (an application may have 20-30 unique path components). HTTP header names and URL parameter names are also atoms (there may be a couple hundred of them at most). 36 | 37 | If in doubt, use a string. 38 | 39 | ### Parsing raw HTTP requests 40 | 41 | The first step is to parse a raw sequence of bytes (stream, `list(code)`, string, atom, iterator or enumerator) into a barely-structured term. The stream of bytes should probably be represented by extending `library(pure_input)` to work with byte sequences other than streams (perhaps by converting them to in-memory streams at first). That way, we can use DCG rules to handle the parsing. 42 | 43 | The "barely-structured" representation is something like this (`ll` for "low-level"): 44 | 45 | ```prolog 46 | request_ll( Method : atom 47 | , Path : string 48 | , Query : maybe(string) 49 | , Header : string 50 | , Body : string 51 | ). 52 | ``` 53 | 54 | A `Body` can nearly always fit into memory. For really huge bodies, we can create a string that's backed by a mmap'd file. 55 | 56 | The lingua franca representation is a term with more structure and greater flexibility on types. Something like: 57 | 58 | ```prolog 59 | request( Method : atom 60 | , Path : list(atom) 61 | , QueryParams : multimap(atom, any) 62 | , Header : multimap(atom, any) 63 | , Body : any 64 | ). 65 | ``` 66 | 67 | The speculative `multimap` type maps a single key to 0 or more values. Predicates for accessing a key's value iterates all possible values on backtracking. 68 | 69 | Here are some notes about each of those fields: 70 | 71 | * `Method` - lowercase atom like `get`, `post`, `delete`, etc. 72 | * `Path` 73 | * original path split on `/` characters 74 | * first, empty path component removed 75 | * list structure allows easy unification like `Path = [user, UserId, name]` 76 | * `QueryParams` 77 | * values are of `any` type 78 | * middleware can "inflate" a value from `string` (original type) into a type with more structure 79 | * `Header` 80 | * same rationale as `QueryParams` 81 | * `Body` - middleware may inflate to a type with more structure 82 | 83 | ## Middleware 84 | 85 | I imagine that all middleware acts as if it were one giant DCG: 86 | 87 | ```prolog 88 | apply_middleware(Request0, Request) --> 89 | first_middleware, 90 | second_middleware, 91 | third_middleware, 92 | ... 93 | final_middleware, 94 | !, 95 | unify_current_state_with(Request). 96 | ``` 97 | 98 | Each middleware may leave choicepoints and they remain until we finally find a request structure that can satisfy them all. This allows a given middleware to support multiple formats for its results. For example, imagine a middleware which converts a `Date` header into one of: `get_time/1` format, `stamp_date_time/3` format, `library(julian)` format. The `Date` middleware can perform all three conversions on backtracking. A downstream middleware just acts as if the value were in the proper type. If that assumption is wrong, backtracking fixes it. 99 | 100 | For performance, the first middleware could `freeze/2` a bunch of type assertions. That way we get quick failure and avoid backtracking over a bunch of work. I suspect that good conventions will develop and backtracking will happen fairly rarely, but it'll be really valuable when it's needed. 101 | 102 | ## Hooks 103 | 104 | There can be hooks (probably handled by a middleware) for converting unstructured headers and parameters into structured ones. For example, 105 | 106 | ```prolog 107 | header_value(date, Raw, D) :- parse_time(rfc_1123, Raw, D). 108 | header_value(date, Raw, D) :- form_time(rfc_1123(Raw), D). 109 | header_value(content_type, Raw, Mime) :- parse_mimetype(Raw, Mime). 110 | ... 111 | header_value(_, Raw, raw(Raw)). 112 | ``` 113 | 114 | Or maybe the backtracking on these hooks generates multiple values for a given key in the `Header` part of the request. A consumer can say `get_header(foo, Val), float(Val)` to indicate that they want a floating point representation of that header. 115 | 116 | ## Routing 117 | 118 | An essential part of web service programming is routing inbound requests to the piece of code that should handle that request. Comments above hint at this idea but I need to specify it in more detail. Fundamentally, this is a predicate mapping a request value to a closure handling that request. 119 | 120 | Consider ideas from `pack(arouter)` which addresses this same problem. 121 | -------------------------------------------------------------------------------- /whitespace-syntax.md: -------------------------------------------------------------------------------- 1 | # Whitespace Sensitive Syntax 2 | 3 | I personally like Prolog’s syntax. It has a good mix of consistency and flexibility. Unfortunately, people love to hate it. And the comma vs full-stop problem is obnoxious in version control tools. I think it would be fairly easy to implement a whitespace sensitive syntax as a strict superset of Prolog syntax which desugars to normal Prolog. 4 | 5 | This code 6 | 7 | ```prolog 8 | append([], A, A). 9 | append([A|B], C, [A|D]) :- 10 | append(B, C, D). 11 | 12 | % naive reverse 13 | reverse([],[]). 14 | reverse([X|Xs], Zs) :- 15 | reverse(Xs, Ys), 16 | append(Ys, [X], Zs). 17 | ``` 18 | 19 | might be written like this 20 | 21 | ``` 22 | append [] A A 23 | append [A|B] C [A|D] 24 | append B C D 25 | 26 | % naive reverse 27 | reverse [] [] 28 | reverse [X|Xs] Zs 29 | reverse Xs Ys 30 | append Ys [X] Zs 31 | ``` 32 | 33 | We’re basically inferring parentheses, commas, full stop and `:-` using rules like this: 34 | 35 | * line starting in column 0 is a clause head 36 | * adjacent lines starting in column 0 imply a full stop ending the first line 37 | * line indented more than the one before implies :- ending the first line 38 | * adjacent lines starting beyond column 0 imply a comma ending first line 39 | * line indented less that the one before implies full stop ending the first line 40 | * first atom in a line is a functor. remaining tokens are arguments 41 | 42 | I'm not sure how disjunctions should look. 43 | 44 | -------------------------------------------------------------------------------- /yaml.md: -------------------------------------------------------------------------------- 1 | # YAML Library 2 | 3 | Parse and generate YAML text. Lists and scalars can be represented in Prolog natively. Maps are more difficult. I could represent them as lists of pairs or using `library(assoc)` or the speculated [library(map)](map.md). Either way, this would be a fantastic place to use quasi quotation since one could do this: 4 | 5 | ```prolog 6 | calculate_age(today, Birth, Age), 7 | YAML = , 15 | save_config_file(‘config.yaml’, YAML). 16 | ``` 17 | 18 | It’s probably best to write a generic parser and generator with DCG. Then implement a quasi quotation library on top of that. 19 | --------------------------------------------------------------------------------