E[[0]]s = (0,s)E[[1]]s = (1,s)E[[read]](m, i, o) = if (empty i) then error, else (hd i, (m, tl i, o))E[[I]](m, i, o) = if m i = unbound then error, else (m I, (m, i, o))E[[E1 + E2]]s = if (E[[E1]]s = (v1,s1) &E[[E2]]s1 = (v2,s2)) then (v1 + v2, s2) else errorE[[fn x => E]]s = fun ninNat.E[[E]](s[n/x])E[[E1 (E2)]]s =E[[E1]]s (E[[E2]]s)

Define *B*: BoolExp -> [**State** -> [[**Value** x
**State**] + {error}]] by:

B[[true]]s = (true,s)B[[false]]s = (false,s)B[[not B]]s = ifB[[B]]s = (v,s') then (not v, s'), else errorB[[E1 = E2]]s = if (E[[E1]]s = (v1,s1) &E[[E2]]s1 = (v2,s2)) then (v1 = v2, s2) else error

Define *C* : Command -> [**State** -> [**State** +
{error}]] by:

__
__

where m' = m[v/I] is identical to m except the value of I is v.C[[I := E]]s = ifE[[E]]s = (v,(m,i,o)) then (m[v/I],i,o) else error

where v.o is the result of attaching v to the front of o.C[[output E]]s = ifE[[E]]s = (v,(m,i,o)) then (m,i,v.o) else error

C[[if E then C1 else C2]]s = ifB[[B]]s = (v,s') then if v thenC[[C1]]s' elseC[[C2]]s' else errorC[[while E do C]]s = ifB[[B]]s = (v,s') then if v then ifC[[C]]s' = s'' thenC[[while E do C]]s'' else error else s' else errorC[[C1; C2]]s = ifC[[C1]]s = error then error elseC[[C2]] (C[[C1]]s)

Notice that definition of **while** is a recursive definition.

Thus, if *B *[[B]] s = True and s' = *C *[[S]] s, then
*C*[[while B do S]]s = *C *[[while B do S]]s'

Solution involves computing what is known as least fixed points.

Denotational semantics gained considerable popularity over last 15 years.

Many people consider denotational semantics as most appropriate for studying meaning of language independent of its implementation.

Good for looking at trade-offs between alternate forms of similar constructs.

**Which is best?**

No good answer, since have different uses.

Complementary definitions. Can be compared for consistency.

Programming language definitions usually still given in English. Formal definitions often too hard to understand or require too much sophistication. Gaining much more acceptance. Now relatively standard intro graduate course in CS curricula.

Some success at using formal semantics (either denotational or operational) to automate compiler construction (similar to use of formal grammars in automatic parser generation).

Semantic definitions have also proved to be useful in understanding new programming constructs.

What is logic programming? -

- Programming based on the notion of logical deduction in symbolic
logic.
- Implementation typically based on mechanisms for automatic theorem proving.

"A constructive proof that for every list L there is a corresponding sorted list S composed of the same elements as L yields an algorithm for sorting a list."

Philosophy is shared by others not working on "logic programming", e.g. Constable at Cornell, Martin-Löf in Sweden, Calculus of Constructions group in France. These groups want to extract (more traditional) program from constructive proofs.

Very-High-Level Languages - non-procedural

State what must be done, not how to do it. Idea is to separate logic from control. (Examples later)

Developed in 1972 by Alain Colmerauer in Marseilles.

*Text has an excellent introduction to PROLOG. I will presume you have read
it and concentrate on examples!*

Often best to start out as thinking of Prolog in terms of language for working with a data base.

**Three types of statements: **

__Facts__ (or hypotheses):

father(albert,jeffrey).

grandparent(X,Z) :- parent(X,Y),parent(Y,Z).(read ":-" as "if")

__Queries__ (or goals):

?-grandparent(X,jeffrey).

Should think of language as non-deterministic (or non-procedural). Looks for all answers satisfying predicate.

father(john,ralph). father(mary,ralph). father(ralph,bill). mother(john,sue). mother(mary,sue). mother(ralph,joan). male(john). male(ralph). male(bill). female(mary). female(sue). /* RULES */ is_mother(M):-mother(Y,M). parent(X,M):-mother(X,M). parent(X,F):-father(X,F). parents(X,M,F):-mother(X,M),father(X,F). sister(X,S):-female(S),parents(X,M,F),parents(S,M,F),S\=X. ancester(X,Y):-parent(X,Y). ancester(X,Y):-parent(X,Z),ancester(Z,Y). /***********************newsort.pro**************************/ /********************Selection Sort*******************/ sel_sort([],[]). sel_sort(L,[Small|R]) :- smallest(L,Small) , delete(Small,L,Rest) , sel_sort(Rest,R) . /* smallest(List, Small) results in Small being the smallest element in List. */ smallest([Small],Small) . smallest([H|T],Small) :- smallest(T,Small) , Small=<H. smallest([H|T],H). /* delete(Elt,List,Result) has Result as List after deleting Elt. */ delete(Elt,[],[]) . delete(Elt,[Elt|T],T) . delete(Elt,[H|T],[H|NuTail]) :- delete(Elt,T,NuTail) . /*******************Insertion Sort********************/ ins_sort([],[]) . ins_sort([H|T],Sorted) :- ins_sort(T,Rest), insert(H,Rest,Sorted) . /* insert(Elt,List,Result) - if List is sorted, Result is obtained by putting Elt where it fits in order in List. */ insert(Elt,[],[Elt]) . insert(Elt,[H|T],[Elt,H|T]) :- (Elt=<H) . insert(Elt,[H|T],[H|Sorted]) :- insert(Elt,T,Sorted) . /* **********Quick Sort************** */ /* sep(List,Key,Less,More) separates the List into the set of elements less than or equal to Key (in Less) and those greater than or equal to Key (in More) */ sep([],Key,[],[]) . sep([H|T],Key,Less,[H|More]) :- (H>Key) , sep(T,Key,Less,More) . sep([H|T],Key,[H|Less],More) :- (H=<Key) , sep(T,Key,Less,More) . quick_sort([],[]) . quick_sort([H|T],Sorted) :- sep(T,H,Less,More) , quick_sort(Less,L_sorted) , quick_sort(More,M_sorted) , concat(L_sorted,[H|M_sorted],Sorted) . /* concat(First, Second, List) results in List = concatenation of First and Second. */ concat([],L,L) . concat([H|T],L,[H|C]) :- concat(T,L,C) . /************************************************ Can take advantage of reversibility since computing with relations rather than functions. ********************Permutations*******************/ append([],L,L) . append([H|T],L,[H|R]) :- append(T,L,R) . permute([],[]) . permute(L,[H|T]) :- append(V,[H|U],L) , /* V,U stand for the part of L before and after H */ append(V,U,W) , permute(W,T) . ***********************************************

Ex.

father(john,ralph). father(mary,ralph). father(ralph,bill). mother(john,sue). mother(mary,sue). mother(ralph,joan). /* RULES */ is_mother(M):-mother(Y,M). parent(X,M):-mother(X,M). parent(X,F):-father(X,F). ancester(X,Y):-parent(X,Y). ancester(X,Y):-parent(X,Z),ancester(Z,Y). ?-ancester(X,joan).backtracking

First succeeds with `X = ralph`

later w/`X= john, mary`

Usually in prefix, can force to be in infix or postfix and give precedence as well.

Ex. +, - ,*,/

2+3*5 abbreviates +(2,*(3,5))

operations are not evaluated

Better to think of operations as forming tags on values -

- forming records -
- don't really compute anything -
- typically uninterpreted -
- very much like user-defined types in ML

Relations

=, \=, <, >, =<, >= (note order of composite relations) are evaluated.

Ex. `digit(N):- N>=0,N<10. `

Example of using operations.

treesort

tree is either nil or is of form ` maketree(tree1,X,tree2)`.

Write program to do treesort! Very much like ML program.

Ex. `area(L,W,A):-A is L*W.`

Can only compute A from L,W - not reversible.