Class 20 Announcements/info Reason review, clarifications Dictionaries Variant Types Options
• Show of hands: how many are still having trouble with installing VSCode for reason? • Preferred type-annotation for functions: • let f : (int => string) = …
• Reason so far: • Basic types: int, float, string, bool, list(int), list('a), function types, … • Builtins: familiar by now • "let" instead of (define…); each "let" opens a new environment. • Type-ascription: (x:int) means "x is an expression whose value has type int"
A few more things about reason • Modules • Complex assignments/matching • Blocks
A first look at modules • We can collect multiple functions into a module (we can put types and values in there too; more later) • Module names start with a capital letter • All the functions in Tank.re become part of the "Tank" module. • So you've been defining modules all along without knowing it! • To refer to a function – say, "testFull" in the Tank module, you write Tank.testFull(…) and Reason finds the relevant file, and uses the definition found there. • You've already done this with List.rev • Alternative: if you're going to be using "Tank" functions a lot, you can say open Tank; testFull(…) so that you no longer need to use the "Tank" prefix.
Assignment • You've seen let a = 4; • You can also use (in assignments or in "switch" expressions) let (a, b) = (2, 4); • … to assign both parts at once • If you want to ignore one of them, you can say let (_, b) = (2, 4); • Works in general. You can also write let [hd, ...tl] = [1, 4, 5, 6]; to get hd -> 1, tl -> [4, 5, 6];
Type definitions • Sometimes you keep using a type over and over. You can name new types! type ibpair = (int, bool); type bignum = list(int); • You can even make new types that allow multiple possibilities, similar to list('a): type pair('a, 'b) = ('a, 'b); type ibpair = pair(int, bool); type matrix('a) = list(list('a));
Type definition limitations • Some type definitions, like "matrix", don't include necessary information, like "I only want nonempty lists, and the inner lists should be nonempty, too" • A perfect place to add this is at the top of the design recipe, in the data definition (just as you add constraints on values in the I/O specification: "myCount, a positive integer"
let expressions • Racket: (let ((x 5) (y 2)) (+ x y)) => 7 • Reason { let x = 5; let y = 2; x + y }; [result: the integer 7] x; [result: Unbound value x]
Block expressions • The block itself has a value , the value of the last item in the block • It defines a "local environment", in which bindings are made temporarily and then forgotten. let k = {let x = 5; let y = 2; x + y}; • k ends up bound to 7; x and y are undefined. • Perfect application: helper functions!
Helpers inside blocks type bignum = list(int); let bnAdd: (bignum, bignum) => bignum = (a, b) => { let bnAddHelper: … = … ; bnAddHelper(a, b, 0) };
Helpers inside blocks type bignum = list(int); let bnAdd: (bignum, bignum) => bignum = (a, b) => { let bnAddHelper: … = … ; bnAddHelper(a, b, 0) };
Quiz: Fast-reverse /* reverse the first list, and append to it the second */ let rec revHelper: (list('a), list('a)) => list('a) = (lst, rest) => switch(lst){ | [] => rest | [hd, ...tl] => revHelper(tl, [hd, ...rest]) }; let rev: list('a) => list('a) = lst => revHelper(lst,[]); Quiz: rewrite let rev … using a block to "hide" revHelper. You don't need to copy any of the green stuff – just write "…"
"Or" types • Mechanism for saying something can be this or that type season = Fall | Winter | Spring | Summer let averageTemp: season => int = fun | Fall => 52 | Winter => 30 | Spring => 45 | Summer => 80; • The "season" type has only four possible values, just as "bool" has only two. • The "constructors" must start with capital letters.
Fancier Or types • Constructors can include some data type news = Alert(string) | Normal(string); type myIntList = Empty | Cons (int, myIntList); • The kind of data can be a parameter! type myList('a) = Empty | Cons ('a, myList('a));
A simple dictionary “data structure”: a list of pairs. let myDict = [(“I”, “je”); (“you”, ”tu”); (“dog”, “chien”); … ] : list(string*string) better: type dict = list(string * string); let myDict = [(“I”, “je”); (“you”, ”tu”); (“dog”, “chien”); … ] :dict; let rec lookup: (string, dict) => string = (term, dictionary) => switch(dictionary) { | [] -> ???; | [(k, v), ... tl] -> if (term == k) then v else lookup(term, tail) };
Using a dictionary type dict = list(string * string); let myDict = [(“I”, “je”); (“you”, ”tu”); (“dog”, “chien”); … ] :dict; let rec lookup: (string, dict) => string = (term, dictionary) => switch(dictionary) { | [] -> ???; | [(k, v), ... tl] -> if (term == k) then v else lookup(term, tail) };
A builtin type for handling success/failure cases! type option('a) = Some('a ) | None • Intended use: • “ None ” means something like “I didn’t find an answer” or “your item isn’t in the data”, etc. • “ Some(x) ” means “I found an answer, and it was x ” • Requires new type-signature for most functions f: int => int becomes f: int => int option For recursive procs, adds slight complexity
Small example let rec lookup: (string, dict) => string = (term, dictionary) => switch(dictionary) { | [] -> None; | [(k, v), ... tl] -> if (term == k) then Some(v) else lookup(term, tail) };
Annoyance • When you look up "dog", you get Some("chien" ) not "chien"
“Sum up all ints in a list except 1s; if empty or nothing but 1s, return None”
let rec sumButOnes:(list(int) => option(int)) = fun | [] => None | [1, ...tl] => sumButOnes(tl) | [hd, ...tl] => switch (sumButOnes(tl)) { | None => Some(hd) | Some(s) > Some (hd + s) };
Recommend
More recommend