Date: 2025-12-24
Author: luciangreenPlease
Starlog is a library that allows developers to write Starlog syntax directly inside normal .pl files and run it immediately, without converting files. Starlog is a variant of Prolog that uses a more functional notation, particularly for built-in predicates where the output parameter is represented using an "is" operator rather than as the last argument of a predicate.
Starlog is a Prolog variant that uses the notation Result is function(Args) instead of Prolog's function(Args, Result). This makes code more readable when working with transformations and operations that produce a result, as the output variable appears first in the expression rather than at the end of a parameter list.
With Starlog, you can write Starlog syntax directly in your Prolog source files, and the library will automatically expand it into equivalent Prolog goals at load-time using goal_expansion and term_expansion hooks.
:- use_module(starlog).
% Write Starlog syntax directly in your code
test(Result) :-
Result is "Hello" : " " : "World".
% It automatically expands to:
% test(Result) :-
% string_concat("Hello", " ", _G1),
% string_concat(_G1, "World", Result).For direct typing in the REPL, use starlog_call/1:
?- use_module(starlog).
?- starlog_call(A is "x":"y").
A = "xy".
?- starlog_call(L is [1] & [2]).
L = [1, 2].
% Method chains for fluent operations
?- starlog_call(R is reverse([1,2,3]) >> length).
R = 3.The library automatically detects and handles variable-bound Starlog goals, allowing you to bind a goal to a variable and execute it later:
?- use_module(starlog).
?- A = (C is no_eval(eval(1+1))), A.
C = 2.
?- B = (D is "Hello" : "World"), B.
D = "HelloWorld".
?- E = (F is [1,2] & [3,4]), E.
F = [1, 2, 3, 4].How it works: When the library detects the pattern Var = (StarlogGoal), Var, it automatically transforms it to Var = (StarlogGoal), starlog_call(Var) during compilation. This ensures that the Starlog expression is properly expanded when executed.
Important notes:
- This pattern detection works in Prolog files and at the interactive REPL
- It does NOT work with
-gcommand-line goals (usestarlog_call/1instead) - For more complex scenarios where the goal is not executed immediately after binding, use
starlog_call/1explicitly:
% When the goal is stored and executed later
process_goal(Goal) :-
% ... other code ...
starlog_call(Goal).
?- MyGoal = (X is "a":"b":"c"), process_goal(MyGoal).
X = "abc".The library provides predicates to explicitly save call results to variables:
Execute a Starlog goal and explicitly return the result in a variable:
?- starlog_call(X is "hello":"world", Result).
Result = "helloworld".
?- starlog_call(Y is [1,2] & [3,4], Result).
Result = [1, 2, 3, 4].
?- starlog_call(Z is reverse([1,2,3]), Result).
Result = [3, 2, 1].Evaluate a Starlog expression and return the result:
?- starlog_eval("x":"y", Result).
Result = "xy".
?- starlog_eval(1+1, Result).
Result = 2.
?- starlog_eval([a] & [b,c], Result).
Result = [a, b, c].Preserve a Starlog expression without evaluation:
?- starlog_no_eval(1+1, Result).
Result = 1+1.
?- starlog_no_eval("hello":"world", Result).
Result = "hello":"world".
?- starlog_no_eval([a] & [b], Result).
Result = [a] & [b].These predicates are useful for:
- Explicitly capturing results for later use
- Building symbolic expressions as data structures
- Template systems and meta-programming
- Functional composition patterns
Important: By default, Starlog evaluates all expressions. The eval() function is only needed when you want to force evaluation inside no_eval() contexts.
The library supports the following Starlog patterns:
Special operators are used for common operations:
- String concatenation:
C is (A : B)- expands tostring_concat(A, B, C) - List append:
C is (A & B)- expands toappend(A, B, C) - Atom concatenation:
C is (A • B)- expands toatom_concat(A, B, C) - List to term:
T is ..=([f,0,1])- expands toT =.. [f,0,1](creates termf(0,1)) - Term to list:
L is =..(f(0,1))- expands tof(0,1) =.. L(creates list[f,0,1])
By default, Starlog evaluates all expressions. This is the standard behavior:
A is 1+1 % A = 2 (arithmetic evaluated)
B is "x":"y" % B = "xy" (concatenation evaluated)
C is [1] & [2] % C = [1,2] (append evaluated)The eval/1 function explicitly marks an expression for evaluation. While this is the default behavior, it becomes useful when you need to force evaluation inside no_eval contexts:
% Normal evaluation (eval is implicit/default)
A is 1+1 % A = 2
% Explicit eval (same result)
B is eval(1+1) % B = 2
% Force evaluation inside no_eval
C is no_eval(eval(1+1)) % C = 2 (inner eval forces evaluation)
D is no_eval("x" : eval("y":"z")) % D = "x":"yz" (nested eval)The no_eval/1 function prevents evaluation of expressions, preserving them as data:
% Preserve arithmetic expressions
A is no_eval(1+1) % A = 1+1 (not 2)
% Preserve Starlog operators
B is no_eval("x":"y") % B = "x":"y" (not "xy")
% Preserve complex expressions
C is no_eval((1+2)*(3+4)) % C = (1+2)*(3+4) (not 21)
% Use eval to selectively evaluate parts
D is no_eval(eval(1+1) + 3) % D = 2 + 3 (only 1+1 is evaluated)This is useful for:
- Storing formulas as data structures
- Manipulating expressions symbolically
- Lazy evaluation patterns
- Template systems
- Selectively evaluating parts of expressions with
eval
Many Prolog predicates can be written in Starlog syntax:
Length is string_length("hello")→string_length("hello", Length)Rev is reverse([1,2,3])→reverse([1,2,3], Rev)Upper is string_upper("hello")→string_upper("hello", Upper)
Nested expressions are automatically decomposed into sequential goals. Functions can now be nested on both the left-hand side (LHS) and right-hand side (RHS) of is:
% Nested functions on RHS (original support)
X is reverse(reverse([1,2,3])) % X = [1,2,3]
X is length(reverse([1,2,3])) % X = 3
% Nested functions on LHS (new support)
reverse(reverse([1,2,3])) is X % X = [1,2,3]
length(reverse([1,2,3])) is X % X = 3
% Nested functions on both sides
reverse(A) is reverse([1,2,3]) % A = [1,2,3]
% Mixed with concat operators
reverse([1,2]&[3,4]) is X % X = [4,3,2,1]
(reverse([1,2])&reverse([3,4])) is X % X = [2,1,4,3]
% Deep nesting
reverse(reverse(reverse([1,2,3]))) is X % X = [3,2,1]
reverse(reverse([1]&[2])&reverse([3]&[4])) is X % X = [3,4,1,2]
% Traditional nested string/atom concat
E is (A:(B:(D • F))) • C
% Expands to:
atom_concat(D, F, _G1),
string_concat(B, _G1, _G2),
string_concat(A, _G2, _G3),
atom_concat(_G3, C, E).All combinations and configurations of nested functions with Starlog operators are supported.
In addition to nested function calls, Starlog supports a fluent method chain syntax using the >> operator. This allows you to chain operations in a left-to-right, more readable style:
% Traditional nested calls
X is d(a(b(1,c)))
% Method chain syntax - reads left to right
X is b(1,c) >> a >> d
% Both produce the same resultThe >> operator creates a pipeline where each operation is applied to the result of the previous operation:
% With built-in functions
X is reverse([1,2,3]) >> length % X = 3
X is sort([3,1,2]) >> reverse % X = [3,2,1]
% With Starlog operators
X is ([1,2]&[3,4]) >> reverse >> length % X = 4
X is ("hello":"world") >> string_length % X = 10
% With user-defined predicates
% Given: wrap_a(X, a(X)), wrap_d(X, d(X))
X is b(1,c) >> wrap_a >> wrap_d % X = d(a(b(1,c)))
% Complex chains
X is 5 >> add_one >> double % X = 12 (if add_one and double are defined)
% Mixed with other Starlog features
X is (reverse([1,2])&reverse([3,4])) >> flatten % X = [2,1,4,3]Key features:
- Left-to-right reading: More natural flow compared to nested calls
- Composable: Works with all value-returning builtins and user predicates
- Equivalent semantics:
b(1,c) >> a >> dis exactly equivalent tod(a(b(1,c))) - Mixes with Starlog operators: Can use
:,&,•in the base expression
Note: Due to SWI-Prolog's dictionary syntax using ., we use >> for method chaining instead of .
Standard arithmetic expressions are left unchanged:
X is 1+2 % Treated as arithmetic, not Starlog
X is Y * 5 % Also arithmeticThe library distinguishes between Starlog and arithmetic based on the right-hand side:
- Starlog:
Out is (A : B),Out is func(Args),Out is (A & B),Out is (A • B),Out is no_eval(Expr),Out is eval(Expr) - Arithmetic:
Out is 1+2,Out is X*Y,Out is sqrt(N)(when sqrt/1 is arithmetic) - Dual Starlog expressions:
(Expr1) is (Expr2)where both sides are Starlog expressions - see below
Note: eval is the default behavior for Starlog expressions. You only need to use eval() explicitly when forcing evaluation inside no_eval() contexts.
When both the left-hand side and right-hand side of is/2 are Starlog expressions, the library compiles both expressions and unifies their results. This enables powerful pattern matching and equation solving:
% Find A and B such that [1]&A equals B&[2]
?- ([1] & A) is (B & [2]).
A = [2],
B = [1].
% Nested dual expressions
?- (([1] & [2]) & A) is (B & [3]).
A = [3],
B = [1, 2].
% String concatenation dual expressions
?- ("hello" : "world") is ("hello" : "world").
true.
% Nested concatenation with multiple variables
?- (A : a : C) is (b : a : c).
A = b,
C = c.
% List with nested concatenation
?- [A:a:C] is [x:a:y].
A = x,
C = y.This feature is particularly useful for:
- Solving equations with Starlog operators
- Pattern matching with variable substitution
- Constraint-based programming with Starlog syntax
- Verifying equivalence of complex expressions
- Extracting multiple variables from nested concatenation patterns
The library supports automatic expansion for many built-in predicates. Here are the main categories:
string_length/1,atom_length/1string_chars/1,atom_chars/1string_upper/1,string_lower/1atom_codes/1,string_codes/1term_string/1,term_to_atom/1sub_string/4,sub_atom/4- And more...
reverse/1,sort/1,flatten/1length/1,member/1intersection/2,union/2,subtract/2nth0/2,nth1/2,last/1min_list/1,max_list/1,sum_list/1- And more...
ceiling/1,floor/1,round/1,truncate/1abs/1,sign/1,sqrt/1sin/1,cos/1,tan/1log/1,exp/1- And more...
findall/2find/2- Execute a goal with cut and collect first solutionterm_variables/1split_string/3date/0,get_time/0(nullary operations)- And more...
For a complete list, see starlog_registry.pl.
The find/2 predicate executes a goal with a cut to get only the first solution. It follows the Starlog pattern of using is for value-returning operations:
% Starlog syntax (recommended):
Result is find(Template, Goal)
% Equivalent to: findall(Template, (Goal, !), [Result])
% with automatic evaluation of Starlog expressions in Template
?- use_module(starlog).
?- starlog_call(Result is find(A, starlog_call([A:a] is [a:a]))).
Result = a.
?- starlog_call(Result is find(X, member(X, [1,2,3]))).
Result = 1. % Only first solution due to cut
?- starlog_call(Result is find(R, starlog_call(R is "hello":"world"))).
Result = "helloworld".
?- starlog_call(Result is find(L, starlog_call(L is [1,2]&[3,4]))).
Result = [1, 2, 3, 4].Note: The old 3-argument form find(Template, Goal, Result) is still supported for backward compatibility.
The find/2 predicate can solve for multiple variables simultaneously in nested concatenation patterns:
% Find two variables in a three-way concatenation
?- starlog_call(Result is find([A,C], starlog_call([A:a:C] is [a:a:c]))).
Result = [a, c].
% Find two variables with a separator
?- starlog_call(Result is find([A,C], starlog_call([A:"_":C] is ["hello":"_":"world"]))).
Result = ["hello", "world"].
% Find three variables in a five-way pattern
?- starlog_call(Result is find([A,C,E], starlog_call([A:"-":C:"-":E] is ["x":"-":"y":"-":"z"]))).
Result = ["x", "y", "z"].
% Works with atom concatenation too
?- starlog_call(Result is find([A,C], starlog_call([A•x•C] is [y•x•z]))).
Result = [y, z].Feature: When the template contains Starlog operators like :, •, or &, find/2 automatically evaluates the template after the goal succeeds:
% Template with string concatenation - automatically evaluated
?- starlog_call(Result is find(A:C, starlog_call([A:d:a:C] is [a:d:a:c]))).
Result = "ac". % Concatenated result, not [a, c]
% Template with atom concatenation - automatically evaluated
?- starlog_call(Result is find(A•C, starlog_call([A•x•C] is [y•x•z]))).
Result = yz. % Concatenated atom, not [y, z]
% Multiple variables in template
?- starlog_call(Result is find(A:"-":C, starlog_call([A:"-":C] is ["x":"-":"y"]))).
Result = "x-y". % Fully evaluated concatenation
% List template - NOT evaluated (backward compatible)
?- starlog_call(Result is find([A,C], starlog_call([A:a:C] is [a:a:c]))).
Result = [a, c]. % List of values, not concatenatedThis is useful for:
- Finding the first solution to a goal
- Executing Starlog expressions and capturing the result
- Solving equations with only the first solution
- Pattern matching with automatic cut behavior
- Extracting multiple values from concatenation patterns
- Collecting computed results directly from templates
You can register your own value-returning builtins:
% Register foo/2 as a value builtin
% Now: Out is foo(A,B) expands to foo(A,B,Out)
?- starlog_register_value_builtin(foo, 2, foo).
% Register with a different Prolog predicate name
?- starlog_register_value_builtin(bar, 1, my_bar).
% Now: Out is bar(X) expands to my_bar(X,Out)The library provides features to convert Prolog code back to Starlog notation with human-friendly variable names (A, B, C, A1, B1, etc.) and pretty printing with proper indentation for nested calls and logical control structures.
The output automatically formats code with indentation for:
- Nested calls (findall, and, or, not)
- Logical control structures (if-then, if-then-else)
- Complex nested expressions
Example:
?- starlog_output_code(findall(X, (member(X, [1,2,3]), X > 1), Result)).
A is
findall(
B,
(
member(B,[1,2,3]),
B>1
)
)Use starlog_output_code/1 to convert a Prolog goal to Starlog notation:
?- starlog_output_code(string_concat("x", "y", C),D).
D = (A is "x":"y").
?- starlog_output_code(append([1,2], [3,4], L),D).
D = (A is [1, 2]&[3, 4]).
?- starlog_output_code(reverse([1,2,3], R),D).
D = (A is reverse([1, 2, 3])).This is useful for:
- Converting existing Prolog code to Starlog notation
- Understanding how Prolog predicates map to Starlog syntax
- Generating Starlog code programmatically
Use starlog_output_code/3 or starlog_output_file/3 with the compress(true) option to maximally compress Starlog code by nesting expressions:
?- starlog_output_code((string_concat("hello", " ", T1),
string_concat(T1, "world", T2)), D, [compress(true)]).
D = (A is "hello":" ":"world").
?- starlog_output_code((append([1],[2],L1), reverse(L1,L2)), D, [compress(true)]).
D = (A is reverse([1]&[2])).The compression algorithm:
- Nests expressions where an intermediate variable is used only once
- Preserves variables that are used multiple times
- Excludes if-then clauses, logical control structures (or, not)
- Excludes calls without an output that is another's input
Example comparison:
% Without compression (default)
?- starlog_output_code((string_concat("a","b",T1),
string_concat("c","d",T2),
string_concat(T1,T2,T3)), D).
D is (A is "a":"b",B is "c":"d",C is A:B)
% With compression
?- starlog_output_code((string_concat("a","b",T1),
string_concat("c","d",T2),
string_concat(T1,T2,T3)), D, [compress(true)]).
D is (A is "a":"b":("c":"d"))Use starlog_output_file/1 to convert an entire Prolog file to Starlog notation:
?- starlog_output_file('my_program.pl').
% Starlog code output for file: my_program.pl
greet(A,B,C):-D is "Hello, ":A,E is D:" ",C is E:B.
combine_lists(A,B,C,D):-C is A&B,D is reverse(C).
...Or with maximal compression using starlog_output_file/3:
?- starlog_output_file('my_program.pl', user_output, [compress(true)]).
% Starlog code output for file: my_program.pl
greet(A,B,C):-C is "Hello, ":A:" ":B.
combine_lists(A,B,C,D):-D is reverse(A&B).
...Or write to a file using starlog_output_file/2 or starlog_output_file/3:
?- open('output.pl', write, Stream),
starlog_output_file('input.pl', Stream),
close(Stream).
% With compression
?- open('output.pl', write, Stream),
starlog_output_file('input.pl', Stream, [compress(true)]),
close(Stream).The output uses human-friendly variable names (A, B, C, ..., Z, A1, B1, ...) making the code more readable.
By default, when outputting Starlog code, the eval() and no_eval() wrappers are stripped from the output, showing only their contents. This makes the output cleaner and more concise, since evaluation is the default behavior in Starlog anyway.
You can control this behavior using the output_eval and output_no_eval options:
% Default behavior - strips both eval() and no_eval()
?- starlog_output_code(A is no_eval(1+1)).
A is 1+1
?- starlog_output_code(B is eval("x":"y")).
B is "xy"
% Note: eval without outputting eval evaluates the expression.
% Keep no_eval() wrappers
?- starlog_output_code(A is no_eval(1+1), _, [output_no_eval(true)]).
A is no_eval(1+1)
% Keep eval() wrappers
?- starlog_output_code(B is eval("x":"y"), _, [output_eval(true)]).
B is eval("x":"y")
% Note: eval with outputting eval doesn't evaluate the expression.
% Keep both wrappers
?- starlog_output_code(C is no_eval(eval(1+1)), _, [output_eval(true), output_no_eval(true)]).
C is no_eval(eval(1+1))The same options work with starlog_output_file/3:
% Default - strips both eval() and no_eval()
?- starlog_output_file('input.pl', user_output).
% Keep no_eval() wrappers
?- starlog_output_file('input.pl', user_output, [output_no_eval(true)]).
% Keep both eval() and no_eval() wrappers
?- starlog_output_file('input.pl', user_output, [output_eval(true), output_no_eval(true)]).Options summary:
output_eval(false)- Stripeval()wrappers (default)output_eval(true)- Keepeval()wrappers in outputoutput_no_eval(false)- Stripno_eval()wrappers (default)output_no_eval(true)- Keepno_eval()wrappers in output
These options can be combined with compress(true) for maximum control over the output format.
The library also provides features to convert Starlog code back to standard Prolog with maximal decompression, using human-friendly variable names (A, B, C, A1, B1, etc.).
Use starlog_to_prolog_code/1 to convert a Starlog goal to standard Prolog:
?- starlog_to_prolog_code(A is "hello":"world").
string_concat("hello","world",A)
?- starlog_to_prolog_code(A is [1,2]&[3,4]).
append([1,2],[3,4],A)
?- starlog_to_prolog_code(A is reverse([1,2]&[3,4])).
append([1,2],[3,4],A),reverse(A,B)This is useful for:
- Converting Starlog code to standard Prolog
- Understanding how Starlog operators map to Prolog predicates
- Generating Prolog code from Starlog notation
The conversion automatically decompresses nested expressions into sequential goals:
% Nested Starlog expression
?- starlog_to_prolog_code(A is "hello":" ":"world",D).
D = (string_concat("hello", " ", A), string_concat(A, "world", B)).
% Deeply nested expression
?- starlog_to_prolog_code(Result is reverse([1]&[2]&[3]),D).
D = (append([1], [2], A), append(A, [3], B), reverse(B, C)).The decompression algorithm:
- Flattens nested expressions into sequential goals
- Maintains proper variable dependencies and execution order
- Uses human-friendly variable names (A, B, C, etc.)
- Preserves semantics of the original Starlog code
?- starlog_to_prolog_code(A is "hello":"world", Code, [print(false)]).
Code = string_concat("hello", "world", A).
Use starlog_to_prolog_file/1 to convert an entire Starlog file to standard Prolog:
?- starlog_to_prolog_file('my_starlog_code.pl').
% Prolog code output for file: my_starlog_code.pl
greet(A,B):-string_concat("Hello, ",A,B).
combine_and_reverse(A,B,C):-append(A,B,D),reverse(D,C).
...Or write to a file using starlog_to_prolog_file/2:
?- open('output_prolog.pl', write, Stream),
starlog_to_prolog_file('input_starlog.pl', Stream),
close(Stream).The library supports bidirectional conversion between Prolog and Starlog:
% Prolog → Starlog (with compression)
?- starlog_output_code((string_concat("hello"," ",T1),
string_concat(T1,"world",T2)), D, [compress(true)]).
D is (A is "hello":" ":"world")
?- starlog_output_code(string_concat(A, B, C), Code).
Code = (A is B:C).
% Starlog → Prolog (with decompression)
?- starlog_to_prolog_code(A is "hello":" ":"world",D).
D is (string_concat("hello"," ",A),string_concat(A,"world",B))
% Convert chain to nested
?- starlog_output_code(
(X is reverse([1,2,3]) >> length),
Code,
[output_style(nested_calls), print(true)]
).
% Output: A is length(reverse([1,2,3]))
% Convert nested to chain
?- starlog_output_code(
(X is length(reverse(sort([3,1,2])))),
Code,
[output_style(method_chaining), print(true)]
).
% Output: A is sort([3,1,2])>>reverse>>length
% With operators
?- starlog_output_code(
(X is ([1,2]&[3,4]) >> reverse >> length),
Code,
[output_style(nested_calls), print(true)]
).
% Output: A is length(reverse([1,2]&[3,4]))Enable debug output to see expansions:
?- starlog_set_debug(true).
?- starlog_call(A is "x":"y").
Expanding goal: A is "x":"y"
Expanded to: string_concat("x","y",A)
A = "xy".:- use_module(starlog).
process_name(First, Last, Full) :-
Full is First : " " : Last.
?- process_name("John", "Doe", Name).
Name = "John Doe".:- use_module(starlog).
combine_and_reverse(A, B, Result) :-
Combined is A & B,
Result is reverse(Combined).
?- combine_and_reverse([1,2], [3,4], R).
R = [4, 3, 2, 1].:- use_module(starlog).
complex_concat(A, B, C, Result) :-
Result is (A:B) : C.
?- complex_concat("Hello", " ", "World", R).
R = "Hello World".:- use_module(starlog).
% Store a formula without evaluating it
store_formula(Formula) :-
F is no_eval(Formula),
format('Stored formula: ~w~n', [F]).
?- store_formula(x*2 + y).
Stored formula: x*2+y
% Preserve arithmetic expressions
?- A is no_eval(1+1).
A = 1+1. % Not evaluated to 2
% Preserve Starlog operators
?- B is no_eval("hello":"world").
B = "hello":"world". % Not concatenated:- use_module(starlog).
% Evaluate is the default behavior
?- A is 1+1.
A = 2.
% Explicit eval (same as default)
?- B is eval(1+1).
B = 2.
% Force evaluation inside no_eval context
?- C is no_eval(eval(1+1)).
C = 2. % Inner expression evaluated despite no_eval
% Nested evaluation in preserved structures
?- D is no_eval("Result: " : eval("x":"y")).
D = "Result:":"xy". % Only eval(...) part is evaluated
% Multiple eval expressions
?- E is no_eval([eval(1+1), eval(2+2), 5]).
E = [2, 4, 5]. % Only eval parts are evaluated
% Complex nested case with lists
?- F is no_eval(eval([1] & [2])).
F = [1, 2]. % List append is evaluated:- use_module(starlog).
% Basic pattern: bind a goal to a variable and execute it
?- A = (C is no_eval(eval(1+1))), A.
C = 2.
% Bind and execute string concatenation
?- B = (D is "Hello" : " " : "World"), B.
D = "Hello World".
% Bind and execute list operations
?- E = (F is reverse([1,2] & [3,4])), E.
F = [4, 3, 2, 1].
% Multiple variable-bound goals
?- G = (H is "first":"part"), G, I = (J is "second":"part"), I.
H = "firstpart",
J = "secondpart".
% For non-immediate execution, use starlog_call/1
process_goal(Goal) :-
write('Processing: '),
starlog_call(Goal),
write('Done!'), nl.
?- MyGoal = (X is "delayed":"execution"), process_goal(MyGoal).
Processing: Done!
X = "delayedexecution".This feature enables:
- Dynamic goal construction and execution
- Storing Starlog expressions as first-class values
- Meta-programming patterns with Starlog syntax
- Deferred execution of Starlog expressions
:- use_module(starlog).
% Solve for variables in list append equations
solve_list_equation :-
([1] & A) is (B & [2]),
format('A = ~w, B = ~w~n', [A, B]).
?- solve_list_equation.
A = [2], B = [1]
% Verify equivalence of expressions
verify_concatenation :-
("hello" : " " : "world") is ("hello " : "world"),
write('Expressions are equivalent!'), nl.
?- verify_concatenation.
Expressions are equivalent!
% Nested dual expressions for complex patterns
nested_pattern :-
(([1] & [2]) & A) is (B & [3]),
format('A = ~w, B = ~w~n', [A, B]).
?- nested_pattern.
A = [3], B = [1, 2]This feature is particularly useful for:
- Solving equations with Starlog operators
- Pattern matching with variable substitution
- Constraint-based programming
- Verifying expression equivalence
:- use_module(starlog).
% Find operator Op where 3 = 1 Op 2
find_operator :-
findall(Op, (member(Op, [+, -, *, /]), Expr =.. [Op, 1, 2], 3 is Expr), Ops),
format('Operators that satisfy 3 = 1 Op 2: ~w~n', [Ops]).
?- find_operator.
Operators that satisfy 3 = 1 Op 2: [+]
% Find value A where 3 = 1 + A
find_value :-
findall(A, (member(A, [1, 2, 3]), 3 is 1+A), As),
format('Values that satisfy 3 = 1 + A: ~w~n', [As]).
?- find_value.
Values that satisfy 3 = 1 + A: [2]
% Find multiple solutions
find_multiple_operators :-
findall(Op, (member(Op, [+, -, *, /]), Expr =.. [Op, 2, 3], 6 is Expr), Ops),
format('Operators that satisfy 6 = 2 Op 3: ~w~n', [Ops]).
?- find_multiple_operators.
Operators that satisfy 6 = 2 Op 3: [*]This pattern (from the problem statement "3 is 1(A is +;-;/)2") demonstrates:
- Operator selection from a set of choices
- Value selection from a set of candidates
- Using
findallwith arithmetic constraints - Solving mathematical equations by testing alternatives
:- use_module(starlog).
% Convert list to term
create_term(List, Term) :-
Term is ..=(List).
?- create_term([f,0,1], T).
T = f(0,1).
% Convert term to list
term_to_list(Term, List) :-
List is =..(Term).
?- term_to_list(foo(a,b,c), L).
L = [foo, a, b, c].
% Roundtrip conversion
?- T is ..=([bar,x,y]), L is =..(T).
T = bar(x, y),
L = [bar, x, y].The library includes an algebra solver that can solve equations by applying operations to both sides to isolate the variable:
:- use_module(algebra_solver).
% Solve the equation from the problem statement
% (Y+5)/2 = 2
?- solve_equation((Y+5)/2 is 2, Y, Solution).
Y = -1,
Solution = -1.
% Simple linear equations
?- solve_equation(3*X+5 is 20, X, Solution).
X = 5,
Solution = 5.
% Division equations
?- solve_equation((X-4)/2 is 3, X, Solution).
X = 10,
Solution = 10.
% Quadratic equations (powers)
?- solve_equation(X**2 is 25, X, Solution).
X = 5.0,
Solution = 5.0.
% Variable on either side
?- solve_equation(10 is 2*X, X, Solution).
X = 5,
Solution = 5.
% Complex nested equations
?- solve_equation(((X+2)*3-1)/2 is 7, X, Solution).
X = 3,
Solution = 3.The solver supports:
- Basic arithmetic operations: +, -, *, /
- Power operations: **
- Nested expressions
- Variable on either side of the equation
- Automatically applies inverse operations to isolate the variable
This is useful for:
- Solving algebraic equations programmatically
- Educational tools for teaching algebra
- Mathematical modeling and constraint solving
- Automated problem solving
The library includes a Gaussian elimination solver for systems of linear equations:
:- use_module(gaussian_elimination).
% Solve a system of equations
% 2x + y + 3z = 10
% 3x + 0y + 1.5z = 4.5
% 0x + 0y + z = 1
?- solve_system([[2, 1, 3, 10], [3, 0, 1.5, 4.5], [0, 0, 1, 1]], Solution).
Solution = [1.0, 5.0, 1].
% With solution type detection
?- solve_system([[2, 1, 5], [1, -1, 1]], Solution, Type).
Solution = [2.0, 1.0],
Type = unique.
% Solve in Starlog syntax
?- Solution is solve_system([[2, 1, 5], [1, -1, 1]]).
Solution = [2.0, 1.0].The Gaussian elimination solver supports:
- Systems with unique solutions
- Underdetermined systems (infinite solutions)
- Detection of inconsistent systems (no solution)
- Partial pivoting for numerical stability
- Integration with Starlog syntax
For more details, see GAUSSIAN_ELIMINATION.md.
This is useful for:
- Solving systems of linear equations programmatically
- Educational tools for teaching linear algebra
- Scientific computing and numerical methods
- Engineering and physics applications
- Clone this repository
- Load the library in your Prolog code:
:- use_module('/path/to/starlog').Or add it to your SWI-Prolog library path.
Run the test suite:
cd tests
swipl -s test_basic.pl
swipl -s test_nested.pl
swipl -s test_arithmetic_is.pl
swipl -s test_mixed_prolog_starlog.pl
swipl -s test_no_eval.pl
swipl -s test_eval.pl
swipl -s test_algebra_solver.pl- SWI-Prolog 8.x or higher
starlog/
README.md # This file
LICENSE # BSD-3 license
Requirements.txt # Original specification
Requirements.md # Additional requirements
starlog.pl # Main library module
starlog_expand.pl # Expander: compile Starlog -> Prolog goals
starlog_registry.pl # Builtin mapping registry + extension hooks
algebra_solver.pl # Algebraic equation solver
demo_output_feature.pl # Demo: Prolog to Starlog conversion
demo_starlog_to_prolog.pl # Demo: Starlog to Prolog conversion
demo_algebra_solver.pl # Demo: Algebra solver
tests/
test_basic.pl # Basic functionality tests
test_nested.pl # Nested expression tests
test_arithmetic_is.pl # Arithmetic preservation tests
test_mixed_prolog_starlog.pl # Mixed Prolog/Starlog tests
test_starlog_to_prolog.pl # Starlog to Prolog conversion tests
test_starlog_to_prolog_file.pl # File conversion tests
test_algebra_solver.pl # Algebra solver tests
BSD-3-Clause License
Original Starlog-Prolog converter concept by luciangreenPlease. This library implements a new approach using SWI-Prolog's goal and term expansion mechanisms to allow direct entry of Starlog syntax in Prolog files.