http://muaddibspace.blogspot.com/2008/01/type-inference-for-simply-typed-lambda.html is a concise definition of the simply typed lambda calculus in Prolog.
It looks okay, but then he purports to assign a type to the Y combinator... whereas in a very real sense the entire purpose of adding types to lambda calculus is to refuse to assign a type to things like the Y combinator.
Can anyone see exactly where his error or -- more likely -- my misunderstanding is?
The Y combinator in its basic form
Y f = (\x -> f (x x)) (\x -> f (x x))
just cannot be typed using the simple type system proposed in the article.
There are other, much easier but meaningful examples that cannot be typed on that level:
Take e.g.
test f = (f 1, f "Hello")
This obviously works for test (\x -> x) but we cannot give the higher-ranked type that was required here, namely
test :: (∀a . a -> a) -> (Int, String)
But even in more advanced type systems like the GHCI extensions of Haskell which allow the above, Y is still hard to type.
So, given the possibility of recursion, we can just define and work using the fix combinator
fix f = f (fix f)
with fix :: (a -> a) -> a
Typing should disallow self application, it should not be possible to find a type for (t t). If it where possible then t would have a type A -> B, and we would have A = A -> B. Since self application is part of Y combinator, its also not possible to give a type to it.
Unfortunately many Prolog systems allow a solution for A = A -> B. This happens on many grounds, either the Prolog system allows circular terms, then the unification will succeed and the resulting bindings can even further be processed. Or the Prolog system does not allow circular terms, then it depends on whether it implements an occurs check. If the occurs check is on, then unification will not succeed. If the occurs check is off, then the unification might succeed but the resulting bindings can not further be processed, most likely leading to stack overflow in printing or further unifications.
So I guess a circular unification of this type happens in the given code by the used Prolog system and it gets unnoticed.
One way to solve the issue would be to either switch on the occurs check or to replace any of the occuring unifications in the code by an explicit call to unify_with_occurs_check/2.
Best Regards
P.S.: The following Prolog code works better:
/**
* Simple type inference for lambda expression.
*
* Lambda expressions have the following syntax:
* apply(A,B): The application.
* [X]>>A: The abstraction.
* X: A variable.
*
* Type expressions have the following syntax:
* A>B: Function domain
*
* To be on the save side, we use some unify_with_occurs_check/2.
*/
find(X,[Y-S|_],S) :- X==Y, !.
find(X,[_|C],S) :- find(X,C,S).
typed(C,X,T) :- var(X), !, find(X,C,S),
unify_with_occurs_check(S,T).
typed(C,[X]>>A,S>T) :- typed([X-S|C],A,T).
typed(C,apply(A,B),R) :- typed(C,A,S>R), typed(C,B,T),
unify_with_occurs_check(S,T).
Here are some sample runs:
Jekejeke Prolog, Development Environment 0.8.7
(c) 1985-2011, XLOG Technologies GmbH, Switzerland
?- typed([F-A,G-B],apply(F,G),C).
A = B > C
?- typed([F-A],apply(F,F),B).
No
?- typed([],[X]>>([Y]>>apply(Y,X)),T).
T = _T > ((_T > _Q) > _Q)
Related
The prolog is supposed to find the order of five statements statements. Everything is working fine but when i call the query solution([A, B, C, D, E]) I get a sandbox error like this:
The Error:
Sandbox restriction!
Could not derive which predicate may be called from
call(C)
all(schoolgirl,[A,B,C,D,E])
solution([A,B,C,D,E])
Full Prolog Program:
all(_,[]).
all(Pred, [X|Xs]):-
P =..[Pred,X],
call(P),
all(Pred,Xs).
distinct([]).
distinct([X|Xs]):-
not(member(X, Xs)), distinct(Xs).
x0r(A, B):-
A, not(B).
x0r(A, B):-
not(A), B.
schoolgirl(betty).
schoolgirl(ethel).
schoolgirl(joan).
schoolgirl(kitty).
schoolgirl(mary).
betty(Snd,Trd):-
x0r(Snd=kitty, Trd=betty).
ethel(Fst, Snd):-
x0r(Fst=ethel, Snd=joan).
joan(Trd, Fith):-
x0r(Trd=joan, Fith=ethel).
kitty(Snd, Forth):-
x0r(Snd=kitty, Forth=mary).
mary(Forth, Fst):-
x0r(Forth=mary, Fst-betty).
solution([Fst, Snd, Trd, Forth, Fith]):-
all(schoolgirl, [Fst,Snd, Trd, Forth, Fith]),
distinct([Fst, Snd, Trd, Forth, Fith]),
betty(Snd, Trd),
ethel(Fst, Snd),
joan(Trd, Fith),
kitty(Snd, Forth),
mary(Forth, Fst).
The Call is
solution([A, B, C, D, E])
Like a commenter said, this seems to be a particular restriction when using SWI-Prolog's browser-based SWISH system (https://swish.swi-prolog.org/). It doesn't want you to use call/1 with terms that it doesn't know enough about.
Fortunately, you can give it a bit more information: You use call/1 in the following context:
P =..[Pred,X],
call(P),
That is, for a call to a predicate Pred with exactly one argument X. There is a more direct syntax for this:
call(Pred, X)
and this is enough to make the error go away, and to make SWISH willing to run your program. (In fact this syntax is a bit more general because it takes exactly one additional argument to be added to the ones already in Pred, so call(f(a), b) would call the goal f(a, b).)
Your query will now die with a somewhat obscure error:
procedure `A-B' does not exist
Reachable from:
call(_1690-betty)
not(A-betty)
x0r(A=mary,B-betty)
mary(A,B)
solution(A)
You have a typo in the definition of your mary/2 predicate.
I have this code:
contradicts(at(X,_),location(X)).
mustContradict(A, B) :- contradicts(A, B).
contradicts/2 is meant to say: if X is at somewhere, then X can't itself be a location.
mustContradict/2 is meant to say: succeed if A and B are contradictory.
When I run it to detect that if you're at a location, you can't be a location (mustContradict(at(Thing,Location),location(Thing)).) -- it succeeds, as it should. On this one, however:
mustContradict(at(Thing,Location),location(Location)).
it also succeeds, with variable assignment Thing=Location.
I could probably mangle a way to make it ensure all variables are identical when trying to match, something like:
A=at(Thing,Location),B=location(Location),
contradicts(A,AsContradiction),
B==AsContradiction.
but then it would fail on the first test, trying to verify that a Thing that is "at" something can't be a location.
What I think I want is to be able to distinguish variables that are already assigned to other variables from those that are so far not matched to anything.
You shouldn't check your predicates with variables (starting with uppercase), but with terms (starting with lowercase).
mustContradict(at(thing,location),location(location)). fails as it should since thing doesn't equal location. In your example you use variables that can be assigned anything.
I have no idea why you are reassigning mustContradict(A, B) :- contradicts(A, B).
you can change contradicts definition to contradicts(at(X,Y),location(X)) :- not(X == Y).
I wonder if this is not what you wanted to achieve:
at(thing, location).
at(thingLocation, location).
location(location).
location(thingLocation).
contradicts(X) :- at(X,_), location(X).
now contradicts(X). succeeds with X = thingLocation
Looks like the solution was to convert all variables to atoms. Only converting those that were shared or repeated meant that the others could bind to inappropriate things (as in the first example), so the behavior didn't change.
Binding them to atoms, as in this code:
contradicts(at(X,_),location(X)).
mustContradict(X,Y) :-
replaceVariablesWithAtoms(X,NewX), replaceVariablesWithAtoms(Y,NewY),
contradicts(NewX,NewY).
replaceVariablesWithAtoms(Term,NewTerm) :-
Term =.. TermAsList,
terms_to_atoms(TermAsList,NewTermAsList),
NewTerm =..NewTermAsList.
terms_to_atoms([],[]).
terms_to_atoms([H|T],[NewH|NewT]) :-
terms_to_atoms(T,NewT),
term_to_atom(H,NewH).
gives the right answer to both queries.
?- mustContradict(at(Thing,Location),location(Thing)).
true.
?- mustContradict(at(Thing,Location),location(Location)).
false.
Wouldn't it be easier just to write it as you have defined the problem statement:
I have this code:
contradicts(at(X,_),location(X)).
mustContradict(A, B) :- contradicts(A, B).
contradicts/2 is meant to say: "if X is at somewhere, then X can't itself be a location."
mustContradict/2 is meant to say: "succeed if A and B are contradictory."
Assuming that your at/2 and location/1 are facts/predicates in your prolog program . . . why not something like this:
contradiction(X) :- at(X,_), location(X) .
I want to solve a typical Prolog problem from here in Ocaml
Connections are represented by following data:
arc(1,2).
arc(2,3).
arc(3,4).
arc(3,5).
arc(2,5).
arc(5,6).
arc(2,6).
One has to find a path given start and end points (solution to be stored in P), e.g.:
?- path(1,6,P).
Prolog code for this is as follows:
path(X,Y,[arc(X,Y)]) :- arc(X,Y).
path(X,Y,[arc(X,Z)|P]) :- arc(X,Z),path(Z,Y,P).
Output will be as:
P = [arc(1, 2), arc(2, 6)] .
I see that that there are many pattern matching capabilities in Ocaml. How can above problem be solved in Ocaml?
Edit: I am not insisting on unification method.
OCaml is not a logic programming language. It is a conventional multi-paradigm, functional, imperative, and object-oriented language. So there are no builtin constructs for solving logical problems (beyond type checking) or anything like this.
However, your problem is a typical graph problem, so you can use any graph library available in OCaml, to solve it, here is the solution implemented using Graphlib's shortest path algorithm, which implements the classical Dijkstra's algorithm
open Core_kernel
open Graphlib.Std
module G = Graphlib.Make(Int)(Unit)
let graph = Graphlib.create (module G) () ~edges:[
1,2,();
2,3,();
3,4,();
3,5,();
2,5,();
5,6,();
2,6,();
]
let () =
match Graphlib.shortest_path (module G) graph 1 6 with
| None -> printf "No path\n"
| Some p ->
Path.edges p |> Sequence.iter ~f:(fun e ->
printf "Arc(%d, %d)\n" (G.Edge.src e) (G.Edge.dst e))
You can install graphlib using opam
opam install graphlib
then, if you put your program in a separate folder and name, let's say, check_path.ml, you can built and run it using ocamlbuild, e.g.
$ ocamlbuild -package graphlib check_path.native --
Arc(1, 2)
Arc(2, 6)
Lets assume I have facts as follows:
airport(nyc,'newyork').
I want want to display a message if the user inputs an airport that doesn't exist.
My Attempt:
isAirport(Air) :-
airport(Air,T),
(var(T) -> true
;
write('Airport not found'),
fail
).
However, this doesn't seem to work.
First let's see what happens if you query a conjunction (the , operator) first:
?- airport(nyc, _), write('found!').
found!
true.
?- airport(abc, _), write('found!').
false.
This means, isAirport(abc) directly fails after trying airport(abc,_) without the rest of your predicate being evaluated. In many cases, you can therefore get by without an explicit if-then-else construct and just write something of the form
predicate(X) :-
first_condition(X),
second_condition(X).
and it will only succeed if both conditions are fulfilled for X.
In case you really want to create some user interface, this is a bit more tricky, because I/O is inherently non-logical, in particular when there is backtracking involved. We usually call a program which behaves like we would expect from a logical formula pure and when it contains non-logical constructs like I/O or the cut operator ! are called impure.
Unfortunately, the if-then-else construct (-> and ;) and negation (\+) are implemented via cut and therefore impure as well. Luckily, most of the time people want a conditional, a pure disjunction is sufficient:
case(1,a).
case(2,b).
We have an automatic branching from the execution mechanism of Prolog:
?- case(X,Y).
X = 1,
Y = a ;
X = 2,
Y = b.
But sometimes we really want to do something that needs the impure constructs, like user input. Then the easiest way to keep the nice logical properties of our program is to separate the task into pure and impure ones:
main :-
uinput(Data),
pure_predicate(Data, Result),
write(Result).
After we have done all the impure parts, Data is unified with the user data we wanted. Let's have a look at the implementation of uinput/1:
uinput(data(Airport,D-M-Y)) :-
format('~nAirport? '),
read(Airport),
( ground(Airport), airport(Airport, _) )
->
(
format('~nDay? '),
read(D),
format('~nMonth? '),
read(M),
format('~nYear? '),
read(Y),
( ground(D-M-Y), isDate(D-M-Y) )
->
true
;
throw(failure('unknown date'))
)
;
throw(failure('unknown airport'))
.
We successively read terms from the input and throw an exception if we can't handle it. For the if-then-else construct to work, we need to take special care. If we compare the two queries:
?- between(1,3,X), write(X).
1
X = 1 ;
2
X = 2 ;
3
X = 3.
and
?- between(1,3,X) -> write(X); false.
1
X = 1.
you can see that the if-then-else is losing solutions. This means we need to make sure that our condition is deterministic. Asking for a user input term to be ground is already a good idea, because without variables, there is only one solution term. Still, a call to one of the data-predicates airport/1 and isDate/1 might generate the same term multiple times or not terminate at all. In this particular case, we just need to make sure that each airport has a unique shortcut name, we can also generate dates without repetition:
airport(nyc, 'New York City').
airport(wdc, 'Washington DC').
isDate(X-Y-Z) :-
between(1,31,X),
between(1,12,Y),
between(1970,2100,Z).
Another trick in the implementation of uinput is that we just succeed with true when we have validated everything. The only effect of is now that Data is instantiated with whatever the user entered.
If we give a dummy implementation of the actual implementation, we can already try the implementation oursevles:
pure_predicate(_Data, Result) :-
% here goes the actual stuff
Result='we have found something awesome'.
On the prompt we can use the pure predicate without trouble:
?- pure_predicate(someinputdata,Y).
Y = 'we have computed something awesome'.
On the other hand, we can also use the full predicate as follows:
?- main(_).
Airport? wdc.
Day? |: 1.
Month? |: 2.
Year? |: 2000.
we have found something awesome
true.
Since we are using read, we have to input prolog terms and terminate with a dot ., but everything worked as expected.
In case the user input fails, we get:
?- main(_).
Airport? bla(X).
ERROR: Unhandled exception: failure('unknown airport')
Please note that we only went through this trouble to actually fail early and give a user message in that case. For the actual computation, this is completely unneccessary.
In the code below you are making false assumption that T will remain unbound in case if airport will not be found in database:
airport(Air, T)
What happens actually is that call to airport(Air, T) will make isAirport(Air) to fail immediately and your var(T) and other parts will not be executed at all.
Try this code instead:
isAirport(Air) :-
airport(Air, _T), ! ; write('Airport not found'), fail.
I was wondering how to do the answer (or first function) to this question in Prolog only using one predicate? The link I'm referring to is here.
Here's an example of what I mean by only calling one predicate:
reverse([X|Y],Z,W) :- reverse(Y,[X|Z],W).
reverse([],X,X).
What are you trying to do and why do you want just one clause for the predicate?
personally I believe that having the ability to write many clauses is one of the great things of prolog and the code is more elegant and readable that way
Anyway, you will probably want to use or. assuming that you have the following clauses:
foo(Arg11,Arg12,Arg13):-
(Body1).
foo(Arg21,Arg22,Arg23):-
(Body2).
foo(Arg31,Arg32,Arg33):-
(Body3).
you should first make sure that every clause has the same arguments (bye bye pattern matching there!):
foo(Arg1,Arg2,Arg3):-
(Body1b).
foo(Arg1,Arg2,Arg3):-
(Body2b).
foo(Arg1,Arg2,Arg3):-
(Body3b).
and then you will use or (;):
foo(Arg1,Arg2,Arg3):-
(Body1b)
; (Body2b)
; (Body3b).
for example,
reverse([X|Y],Z,W):-
reverse(Y,[X|Z],W).
reverse([],X,X).
will become:
reverse(X,Y,Z):-
X = [H|T],
reverse(T,[H|Y],X).
reverse(X,Y,Z):-
X = [],
Z = Y.
and then:
reverse(X,Y,Z):-
(X = [H|T],
reverse(T,[H|Y],X) )
; (X = [],
Z = Y). *%ew ew ugly!*
regarding the example on your first post, there are two different predicates, each having just one clause. the second predicate has 2 arguments while the first one has 1 therefore they are different. The only way to "merge" them would be by simply calling the second one as has_equal_sums(List, _) in the place of the first.
To be honest, I dont see the point of doing this; I doubt you will not get any significant speedup and the code becomes way messier.
Of course, it's your code and there can be restrictions we dont know (that's why I asked what you want to accomplish)