324 Lecture Week 10 - Winter 2006 ================================= (portions are based on Sheila McIlraith's notes) ML == Developed at Edinburgh (early '80s) as Meta-Language for a program verification system Now a general purpose language There are two basic dialects of ML: - Standard ML (1991) & ML 2000 - Caml(including Objective Caml, or OCaml) ML is a pure functional language - Based on typed lambda calculus - Grew out of frustration with Lisp! - Major programs can be written w/o variables Widely accepted - reasonable performance (claimed) - can be compiled - syntax not as arcane as LISP (nor as simple...) ML main features: Functional Language HOFs, recursion strongly encouraged, etc. Combination of Lisp and Algol features Strong, static typing w/ type inference Quite a fancy type system! Polymorphism a function can take arguments of various types Abstract & recursive data types supported through an elegant type system, the ability to construct new types, and constructs that restrict access to objects of a given type through a fixed set of ops defined for that type. Pattern matching Function as a template Exception handling Allow you to handle errors/exception Elaborate module system Most highly developed of any language Each ML expression has a type associated w/ it. Interpreter builds the type expression Cannot mix types in expressions Must explicitly coerce/type-case e.g. real(2) + 3.0 : real Data types (w/ operators): Basic: unit, bool, integer, real, string Constructors: list, tuple, array, record, function operators: infix, can be overloaded. Read-eval-print Compiler infers type before compiling & executing. E.g., (- is the sml prompt) - (5+3)-2; val it = 6 : int - if 5>3 then "Bob" else "Carol"; val it = "Bob": string - 5<4; val it = false : bool Pattern matching is powerful: Allows programmers to see the arguments No more heads and tails (cars/cdrs) Tupple pattern matching ----------------------- - val v=((2, "Test"),(3.2,#"A")); val v = ((2,"Test"),(3.2,#"A")) : (int * string) * (real * char) - val((i,s),(r,c))=v; val i = 2 : int val s = "Test" : string val r = 3.2 : real val c = #"A" : char - val(p1,p2)=v; val p1 = (2,"Test") : int * string val p2 = (3.2,#"A") : real * char - val(_,(r,_))=v; (*_ ("don't care") matches anything!*) val r = 3.2 : real Functions: ---------- A function maps a type to another one: accepts only one argument. What if we need multiple arguments? fun () =; Eg. - fun fahrToCelsius t = (t - 32) * 5 div 9; val fahrToCelsius = fn : int -> int Multiple-clause function definition (Lazy, the first pattern that matches the actual parameter will be chosen.) - fun len [] = 0 = | len (head::tail) = 1 + len tail; val len = fn : 'a list -> int - fun fib 0 = 0 = | fib 1 = 1 = | fib n = fib(n-1) + fib(n-2); val fib = fn : int -> int Functions are first-class in ML too. "fn" acts like "lambda" in Scheme. Logic Programming and Prolog (= PROgramming LOGic) ============================ Logic programming languages are not procedural or functional. - Specify "relations" between objects - larger(3,2). - father(tom,jane). - Separate logic from control: - Programmer declares WHAT facts and relations are true. - System determines HOW to use facts to solve problems. - System INSTANTIATES variables in order to make relations true! - Computation engine: theorem-proving and recursion (unifications, resolutions, backward chaining, backtracking) - higher-level than imperative languages Relation is formula in Predicate Logic - functor and ordered list of k parameters RESOLUTION: an inference rule to allow inferred propositions to be computed from given propositions - algorithmically, allows theorem proving! Eg. B <- A, D <- C are two propositions, with B identical to C. can rename B and C as T, rewrite props, and it's logically obvious that D <- A More complex if we allow variables. Determining useful values for variables is called UNIFICATION, the temporary assigning of values to variables is called INSTANTIATION. Common to instantiate and fail to complete matching, requiring backtracking. Idea is to disprove the negation of formula. Suppose we state these facts: male(albert). female(alice). male(edward). female(victoria). parent(albert,edward). parent(victoria,edward). parent(albert,alice). parent(victoria,alice). Then we can make queries: ?- male(albert). Yes ?- male(victoria). No ?- female(Person). Person = alice ; Person = victoria ; Person = susan ; No ?- parent(Person, edward). Person = albert ; Person = victoria ; No ?- parent(Person, edward), female(Person). Person = victoria ; No We can also state rules, such as: sibling(X,Y) :- parent(P,X), parent(P,Y). Then the queries become more interesting: ?- sibling(albert,victoria). No ?- sibling(edward, Sib). Sib = edward ; Sib = alice ; Sib = edward ; Sib = alice ; No Prolog vs. Scheme In Scheme, we program with functions (procedures) - a functions arguments are different from the function's value - given a single Scheme function, we can only ask one kind of question: Here are the argument values, tell me the fucntion's value In Prolog, we program with relations - there is no bias; all arguments are the same. - given a single Prolog predicate, we can ask many kinds of questions: Here are some of the argument values, tell me what the others have to be in order to make a true statement. In logic programming, - a program consists of facts and rules - running a program means asking queries - the language tries to find one way (or more) to prove that the query is true - this may have the side effect of freezing variable values - the language determines how to do all of this, not the program - how does the language do it? Using unification, resolution, and backtracking Prolog syntax ------------- Lexical rules: - variables are capitalized - constants begin with a lower case letter - predicate names begin with a lower case letter A query is a fact yet to be proven - if the query has no variables, returns yes/no - if the query has variables, returns appropriate values of variables (called a substitution) [[stopped here--the rest might be useful to read, and we'll cover it next week]] Horn clauses - logically, is c <== a_1 & a_2 & ... & a_n - antecedents: conjunction of zero or more conditions (atomic formulae in predicate logic) - consequent: an atomic formula in PL Meaning: "the consequent c is true if the antecedents a_1,...,a_n are all true Terminology: Horn clause = clause consequent = goal = head antecedents = subgoals = tail Horn clause with no tail = fact Horn clause with tail = rule Writen "c :- a1, ..., an." in Prolog A fact: male(albert). A rule: father(albert,edward) :- male(albert), parent(albert,edward). Variables may appear in antecedents and consequent of a Horn clause: c(X_1, ..., X_n) :- h(X_1, ..., X_n, Y_1, ..., Y_m). means for all values X_1, ..., X_n, the formula c(X_1, ..., X_n) is true if there exist values Y_1, ..., Y_m such that the formula h(X_1, ..., X_n, Y_1, ..., Y_m) is true. Eg. isamother(X) :- female(X), parent(X,Y). ?- isamother(X). X = victoria ; X = victoria ; No