1 / 114

CS51: Introduction to Objective Caml

CS51: Introduction to Objective Caml. Greg Morrisett. Ocaml is a functional language. Small, orthogonal core based on the lambda-calculus. Control is based on (recursive) functions. Instead of for-loops, while-loops, do-loops, iterators , etc.

irma
Télécharger la présentation

CS51: Introduction to Objective Caml

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. CS51: Introduction to Objective Caml Greg Morrisett

  2. Ocaml is a functional language • Small, orthogonal core based on the lambda-calculus. • Control is based on (recursive) functions. • Instead of for-loops, while-loops, do-loops, iterators, etc. • rather, we can define these as library (functions). • Makes it easy to define semantics (and learn!) • Supports higher-order, lexically-scoped, first-class procedures. • a.k.a. closures or lambdas. • first-class: can be passed to other procedures as arguments, returned from procedures as results, placed in data structures etc. • lexically-scoped: meaning of variables determined statically. • higher-order: compositional, programs that build programs. These aspects are in common with other functional languages such as Scheme, Haskell, SML, Clojure, CoffeeScript.

  3. Ocaml is mostly pure. • As opposed to mostly imperative languages. • C/C++, Java, Python, Basic, Fortran, … • Emphasis on values, not variables or state: • “value-oriented programming” • build new values out of old, instead of destructively updating the old. • leads to a simple analytic, computational model • in particular, you can reason locally about pure code. • you can test locally and be assured environment doesn’t break test. • important for scale – even in languages like Java, you should try to avoid imperative updates unless you need them (see J.Bloch’s book). • increasingly important in age of concurrency • Still provides for mutable data structures and I/O. • needed to obtain asymptotic efficiency for some algorithms (e.g., unification.) • but less frequently than you might first suspect.

  4. Ocaml is call-by-value • Most languages today are call-by-value. • arguments to procedures are evaluated to values. • then the values are passed to the procedure • e.g., f (3+4, 5+3) f (7, 8) • Some languages are call-by-name. • arguments are re-computed each time they are used. • pure lambda calculus, Algol, the C pre-processor, TeX • Some languages are call-by-need (a.k.a. lazy) • arguments are computed once, but only on demand. • Haskell, Unix pipes • We will see some uses of laziness later in the class --- it’s a useful pattern to understand and utilize.

  5. Laziness can be a virtue…

  6. XKCD on Laziness

  7. Ocaml is strongly, statically typed • Statically typed: • compiler catches many silly errors before you can run the code. • e.g., calling a function with the wrong number of arguments • Java is also strongly, statically typed. • Scheme, Bash, Python, Javascript, Basic, etc. are all strongly, dynamically typed – type errors are discovered while the code is running. • Strong typed: compiler enforces type abstraction. • cannot cast an integer to a record, function, string, etc. • so we can utilize types as capabilities. • crucial for local reasoning at the level of the language. • C/C++ are weakly-typed languages. The compiler will happily let you do something smart (more often stupid).

  8. Static Cat

  9. A bit of history Alonzo Church: lambda calculus1930’s Guy Steele & Gerry Sussman: Scheme late 1970’s Xavier Leroy:Ocaml1990’s Robin Milner, MadsTofte, & Robert Harper Standard ML 1980’s John McCarthy: LISP 1958 Don Syme:F#2000’s

  10. A bit of fun: http://www.malevole.com/mv/misc/killerquiz/

  11. Installing, running Ocaml • We run Ocaml inside a virtual machine appliance. • same as in CS50. • instructions for installing part of first problem set. • helps to provide uniform environment. • you can bypass this and install Ocaml directly, but we don’t want to have to help you debug your setup, so recommend using the appliance. • Ocaml comes with an interactive, top-level loop. • useful for testing and debugging code. • “ocaml” at the prompt. • It also comes with compilers • “ocamlc” – fast bytecode compiler • “ocamlopt” – optimizing, native code compiler • command line interface similar to GCC • And many other tools • e.g., debugger, dependency generator, profiler, etc.

  12. Editing Ocaml Programs • Many options: pick your own poison • Emacs + Tuareg mode • what I’ll be using in class. • good but not great support for Ocaml. • on the other hand, it’s still the best code editor I’ve encountered. • (extensions written in elisp – a functional language!) • vim, Gedit, etc. • barebones support. • Visual Studio • syntax highlighting and so forth for F#. • warning: we will be sticking to Ocaml, so I don’t recommend this. • Eclipse • I’ve put up a link to an Ocaml plugin (but haven’t tried it).

  13. XKCD on Editors

  14. An Example: (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  15. An Example: Comments start with “(*” and end with “*)”. (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  16. An Example: Top-level definitions start with “let” and if they are recursive “rec” Top-level definitions end with a double-semicolon “;;” (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  17. An Example: try … with Sys_error _ -> e tries to execute the “…” If the Sys_error exception is thrown, we jump to here and immediately return an empty list [ ]. (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  18. An Example: We declare local variables using “let x = … in …” (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  19. An Example: Here we are calling the “concat” function which is in the Filename module. I found it by looking in the Ocaml Reference Manual. (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  20. An Example: The Sys.file_exists function tells us if the file exists. If so, we return a list containing just that file’s full name. (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  21. An Example: If path points to a directory, we want to process all of the files in that directory, including all of the nested directories. (What isn’t quite right here?) (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  22. An Example: We start by getting an array of file names in the directory “path” using the Sys.readdir function. (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  23. An Example: And then convert the array to a list. (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  24. An Example: Notice that (Filename.concat path) is only partially applied. It’s List.map’s job to supply the next argument, which it does for each file name in the list. Here, we are iterating over the list of files, concatenating the path to each file name, and getting a new list of paths. (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  25. An Example: This iterates over all the paths, calling (find name) on them recursively, which means we get back a list of lists of full paths. (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  26. An Example: Finally, we convert the list of lists of full paths into a single list of full paths by calling List.flatten. (* find "name" "path": tries to find all occurrences of files with the given name, starting at the given path, recursively exploring all sub- directories. Returns a list of the full paths to the files. *) let recfind name path = try let fullname = Filename.concat path name in if Sys.file_existsfullnamethen [ fullname ] else if Sys.is_directory path then let files = Array.to_list (Sys.readdir path) in let paths = List.map (Filename.concat path) files in let results = List.map (find name) paths in List.flatten results else [] with Sys_error _ -> [] ;;

  27. Primitive types, values, operations Type: Values: Example Expressions: int -2, 0, 42 42 * (13 + 1) float 3.14, -1., 2e12 (3.14 +. 12.0) *. 10e6 char ‘a’, ’b’, ’&’ int_of_char ‘a’ string “moo”, “cow” “moo” ^ “cow” bool true, false if isduck then 3 else 4 unit () () For more primitive types and functions over them, see the Ocaml Reference Manual here: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html

  28. Expressions exp ::= value numbers, strings, bools, etc. | id variables (x, foo, etc.) | exp1 op exp2 x+3 or path ^ “/usr” | id exp1 exp2 … expnfunction call (foo 3 42) | let id = exp1in exp2 local variable decl. | if exp1then exp2else exp3a conditional Note: this is the abstract syntax; it doesn’t reflect the precedence of operators or the grouping you can do with parentheses (issues of concrete syntax.)

  29. Expressions This is an example of Backus Naur Form (BNF) notation which is often used to describe the structure of a programming language. exp ::= value numbers, strings, bools, etc. | id variables (x, foo, etc.) | exp1 op exp2 x+3 or path ^ “/usr” | id exp1 exp2 … expnfunction call (foo 3 42) | let id = exp1in exp2 local variable decl. | if exp1then exp2else exp3a conditional Note: this is the abstract syntax; it doesn’t reflect the precedence of operators or the grouping you can do with parentheses (issues of concrete syntax.)

  30. Expressions Here, we are describing what are valid Ocaml expressions as a recursive definition. exp ::= value numbers, strings, bools, etc. | id variables (x, foo, etc.) | exp1 op exp2 x+3 or path ^ “/usr” | id exp1 exp2 … expnfunction call (foo 3 42) | let id = exp1in exp2 local variable decl. | if exp1then exp2else exp3a conditional Note: this is the abstract syntax; it doesn’t reflect the precedence of operators or the grouping you can do with parentheses (issues of concrete syntax.)

  31. Expressions The different alternatives are separated by a vertical bar (“|”). So an expression is either (1) a value, or (2) an expression, operator, and another expression, or (3) an identifier followed by a list of expressions (separated by spaces), or… exp ::= value numbers, strings, bools, etc. | id variables (x, foo, etc.) | exp1 op exp2 x+3 or path ^ “/usr” | id exp1 exp2 … expnfunction call (foo 3 42) | let id = exp1in exp2 local variable decl. | if exp1then exp2else exp3a conditional Note: this is the abstract syntax; it doesn’t reflect the precedence of operators or the grouping you can do with parentheses (issues of concrete syntax.)

  32. Expressions It’s just a very succinct way to write down a definition for an infinite family of things. As we’ll see, ML provides a way to do this for data structures that makes it very easy to manipulate “language-like” things. exp ::= value numbers, strings, bools, etc. | id variables (x, foo, etc.) | exp1 op exp2 x+3 or path ^ “/usr” | id exp1 exp2 … expnfunction call (foo 3 42) | let id = exp1in exp2 local variable decl. | if exp1then exp2else exp3a conditional Note: this is the abstract syntax; it doesn’t reflect the precedence of operators or the grouping you can do with parentheses (issues of concrete syntax.)

  33. A note on parentheses In most languages, one writes parentheses around comma-separate arguments: f(x,y,z) sum(3,4,5) In Ocaml, we don’t bother with parentheses or the commas: f x y z sum 3 4 5 But we do have to worry about grouping. For example, these two expressions mean something different: f x y zf x (y z) The first one passes three arguments to f (x, y, and z) whereas the second passes two arguments to f (x, and the result of applying the function y to z.) When in doubt group things with parentheses.

  34. More Ocaml: compound types • Tuples (anonymous records, cartesian products): • (exp1, exp2, …, expn) (1,true,”greg”) • has type t1*t2*…*tnint*bool*string • To eliminate: let (id1,id2,…,idn) = e1 in e2 • For example: let p = (1,3) in let (x,y) = pin x+y 4

  35. Better Example: let distance p1 p2 = let (x1,y1) = p1 in let (x2,y2) = p2 in sqrt (square (x2 –. x1) +. (square (y2 –. y1)) distance (3.0,5.0) (4.0,7.0)  2.23606797749979

  36. More Ocaml: Options • A value v is a T option if v is either Some(v’) where v’ is a value of type T, or else v is None. • Options are useful for signaling there’s no useful answer. • For example, we might write: let slope p1 p2 = let (x1,y1) = p1 in let (x2,y2) = p2 in if x2 –. x1 = 0.0 then None else Some((y2 –. y1) /. (x2 -. x1)) • As another example, we might call lookup in a hash-table and return None when there is no value associated with the key, and Some(v) when v is associated with the key.

  37. Working with options • How do we use an option? • it could be None or it could be Some(v) • furthermore, we want to get at the value v when it’s Some(v). • We must use a match expression to handle both possible cases. match lookup table key with | None -> Printf.printf “key not found” | Some v -> Printf.printf “value is %d\n” v

  38. Let vs. Match You can also tear apart a tuple using match: let distance p1 p2 = match p1 with | (x1,y1) -> match p2 with | (x2,y2) -> sqrt (square (x2 –. x1) +. (square (y2 –. y1)) There’s only one case for tuples, which is why we can use “let” in place of this.

  39. An alternative: Matching can nest: let distance p1 p2 = match (p1,p2) with | ((x1,y1),(x2,y2)) -> sqrt (square (x2 –. x1) +. (square (y2 –. y1)) We are matching (p1,p2) --- a pair of pairs of floats:(float * float) * (float * float)

  40. Lists: • Lists • [] the empty list • exp1::exp2add exp1 to the front of list exp2 • The list of numbers from 0 to 3: 0::1::2::3::[] • Parses as 0::(1::(2::(3::[]))) • We pronounce “::” as “cons”. • We say exp1 is the head of the list, and exp2 is the tail of the list. • Occasionally we will slip and say “car” for head, and “cdr” for tail. • [exp1;exp2;…;expn] shorthand forexp1::(exp2::(…::expn::[])…) [“Greg”;”Tanya”;”Amy”;”John”] ;; “Greg”::”Tanya”::”Amy”::”John”::[]

  41. Tearing apart a list Just as with options, there are two cases for lists: it could be empty ([ ]) or it could be a cons (e1 :: e2). let head x = match xwith | [] -> None | hd::tl -> Some hd ;;

  42. Tearing apart a list The underscore (“_”) is a pattern that matches anything without giving it a name. Just as with options, there are two cases for lists: it could be empty ([ ]) or it could be a cons (e1 :: e2). let head x = match xwith | [] -> None | hd::_ -> Some hd ;;

  43. A more interesting example • Given a list of pairs of integers, produce the list of products of the pairs. • e.g., Given [(2,3); (4,7); (5,2)] return [6; 28; 10]

  44. A more interesting example Start by writing the types of the function. • Given a list of pairs of integers, produce the list of products of the pairs. • e.g., Given [(2,3); (4,7); (5,2)] return [6; 28; 10]

  45. A more interesting example Tear apart the input. • Given a list of pairs of integers, produce the list of products of the pairs. • e.g., Given [(2,3); (4,7); (5,2)] return [6; 28; 10] let recprods (l : (int*int) list) : int list =

  46. A more interesting example The pair of ints (x,y) is the head of the list, and tl is the tail of the list. • Given a list of pairs of integers, produce the list of products of the pairs. • e.g., Given [(2,3); (4,7); (5,2)] return [6; 28; 10] let recprods (l : (int*int) list) : int list = match lwith | [] -> | (x,y)::tl ->

  47. A more interesting example Solve the first case (easy). • Given a list of pairs of integers, produce the list of products of the pairs. • e.g., Given [(2,3); (4,7); (5,2)] return [6; 28; 10] let recprods (l : (int*int) list) : int list = match lwith | [] -> | (x,y)::tl ->

  48. A more interesting example Solve the second case. Pay attention to the return type. • Given a list of pairs of integers, produce the list of products of the pairs. • e.g., Given [(2,3); (4,7); (5,2)] return [6; 28; 10] let recprods (l : (int*int) list) : int list = match lwith | [] -> [] | (x,y)::tl ->

  49. A more interesting example We know it has to be an int list. So ?1 should be an int, and ?2 should be an int list. • Given a list of pairs of integers, produce the list of products of the pairs. • e.g., Given [(2,3); (4,7); (5,2)] return [6; 28; 10] let recprods (l : (int*int) list) : int list = match lwith | [] -> [] | (x,y)::tl -> ?1 :: ?2

  50. A more interesting example • Given a list of pairs of integers, produce the list of products of the pairs. • e.g., Given [(2,3); (4,7); (5,2)] return [6; 28; 10] let recprods (l : (int*int) list) : int list = match lwith | [] -> [] | (x,y)::tl -> (x*y) :: ?2

More Related