├── .gitignore ├── README.org ├── homework ├── README.org ├── hw0 │ ├── hw0.sml │ └── hw0test.sml ├── hw1 │ ├── assignment1.pdf │ ├── hw1.sml │ └── hw1test.sml ├── hw2 │ ├── assignment2.pdf │ ├── hw2.sml │ └── hw2test.sml ├── hw3 │ ├── assignment3.pdf │ ├── hw3.sml │ └── hw3test.sml ├── hw4 │ ├── assignment4.pdf │ ├── curry.jpg │ ├── dan.jpg │ ├── dog.jpg │ ├── dog2.jpg │ ├── hw4.rkt │ ├── hw4combined_test_with_graphisc.rkt │ └── hw4test.rkt ├── hw5 │ ├── assignment5.pdf │ ├── hw5.rkt │ └── hw5test.rkt ├── hw6 │ ├── assignment6.pdf │ ├── hw6assignment.rb │ ├── hw6graphics.rb │ ├── hw6provided.rb │ └── hw6runner.rb └── hw7 │ ├── assignment7.pdf │ ├── hw7.rb │ ├── hw7.sml │ ├── hw7testprovided.rb │ └── hw7testprovided.sml └── resources ├── 01code ├── 10_repl.txt ├── 11_list_functions.sml ├── 12_let_expressions.sml ├── 13_nested_functions.sml ├── 14_let_efficiency.sml ├── 15_options.sml ├── 3_our_first_program.sml ├── 5_errors_fixed.sml ├── 5_some_errors.sml ├── 6_shadowing.sml ├── 7_functions.sml └── 9_tuples.sml ├── 01summary.pdf ├── 02code ├── 21_records.sml ├── 22_tuples_as_syntactic_sugar.sml ├── 23_datatype_bindings.sml ├── 24_case_expressions.sml ├── 25_useful_datatypes.sml ├── 27_type_synonyms.sml ├── 28_another_expression_example.sml ├── 29_list_and_option_datatypes.sml ├── 30_polymorphic_datatypes.sml ├── 31_eachof_pattern_matching.sml ├── 32_type_inference.sml ├── 33_polymorphic_and_equality_types.sml ├── 34_nested_patterns.sml ├── 35_more_nested_patterns.sml ├── 37_function_patterns.sml ├── 38_exceptions.sml ├── 39_tail_recursion.sml └── 40_accumulators.sml ├── 02summary.pdf ├── 03code ├── 42_functions_intro.sml ├── 43_functions_as_arguments.sml ├── 44_types_and_functions.sml ├── 45_anonymous_functions.sml ├── 46_unnecessary_function_wrapping.sml ├── 47_map_and_filter.sml ├── 48_generalizing_prior_topics.sml ├── 49_lexical_scope.sml ├── 50_lexical_scope_and_functions.sml ├── 51_why_lexical_scope.sml ├── 52_closures_and_recomputation.sml ├── 53_fold_and_more_closures.sml ├── 54_combining_functions.sml ├── 55_currying.sml ├── 56_partial_application.sml ├── 57_currying_wrapup.sml ├── 58_mutable_references.sml ├── 59_callbacks.sml ├── 61_adts_with_closures.sml ├── 62_without_closures.sml ├── 63_java_without_closures.java └── 64_c_without_closures.c ├── 03summary.pdf ├── 04code ├── 66_what_is_type_inference.sml ├── 67_ml_type_inference.sml ├── 68_type_inference_examples.sml ├── 69_polymorphic_examples.sml ├── 70_other_inference.sml ├── 71_mutual_recursion.sml ├── 72_namespace_mgmt.sml ├── 73_signatures.sml ├── 74_module_example.sml ├── 75_signatures_for_example.sml ├── 77_equivalent_structure.sml ├── 78_another_equivalent_structure.sml └── 79_modules_different_types.sml ├── 04summary.pdf ├── 05code ├── 100_memoization.rkt ├── 101_macros_intro.rkt ├── 103_define_syntax.rkt ├── 105_macro_examples.rkt ├── 83_racket_intro.rkt ├── 84_racket_basics.rkt ├── 85_racket_lists.rkt ├── 87_parens_matter.rkt ├── 88_dynamic_typing.rkt ├── 89_cond.rkt ├── 90_local_bindings.rkt ├── 91_toplevel_bindings.rkt ├── 92_setbang.rkt ├── 93_truth_about_cons.rkt ├── 94_mcons.rkt ├── 95_thunks.rkt ├── 96_avoid_unnecessary.rkt ├── 97_delay_force.rkt ├── 98_using_streams.rkt └── 99_defining_streams.rkt ├── 05summary.pdf ├── 06code ├── 107_datatypes_without_structs.rkt ├── 107_datatypes_without_structs.sml ├── 108_datatypes_with_structs.rkt ├── 111_interpreter_assumptions.rkt ├── 115_macros_via_functions.rkt └── _523523785cf7a0b9a3aa908e8d0345c7_section7_video_code_files │ ├── 120_static_vs_dynamic_one.rkt │ ├── 120_static_vs_dynamic_one.sml │ └── 122_eval.rkt ├── 06summary.pdf ├── 07code ├── 120_static_vs_dynamic_one.rkt ├── 120_static_vs_dynamic_one.sml └── 122_eval.rkt ├── 07summary.pdf ├── 08code ├── 123_ruby_intro.rb ├── 124_classes_objects.rb ├── 125_object_state.rb ├── 127_example.rb ├── 129_classes_dynamic.rb ├── 131_arrays.rb ├── 132_blocks.rb ├── 133_using_blocks.rb ├── 134_procs.rb ├── 135_hashes_ranges.rb ├── 136_subclassing.rb ├── 138_overriding.rb ├── 140_dynamic_dispatch_vs_closures.rb ├── 140_dynamic_dispatch_vs_closures.sml └── 141_manual_dynamic_dispatch.rkt ├── 08summary.pdf ├── 09code ├── 142_fp_oo_decomposition.java ├── 142_fp_oo_decomposition.rb ├── 142_fp_oo_decomposition.sml ├── 143_adding_operations_or_variants.java ├── 143_adding_operations_or_variants.rb ├── 143_adding_operations_or_variants.sml ├── 144_binary_methods_fp.sml ├── 145_double_dispatch.java ├── 145_double_dispatch.rb ├── 147_multiple_inheritance.rb └── 148_mixins.rb ├── 09summary.pdf ├── 10summary.pdf └── README.org /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | 3 | TODO.org 4 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Programming Languages 2 | 3 | Programming assignments for Programming Languages course on Coursera provided 4 | by University of Washington. 5 | 6 | + [[https://www.coursera.org/learn/programming-languages/][Programming Languages Part A, (in SML)]] 7 | + [[https://www.coursera.org/learn/programming-languages-part-b][Programming Languages Part B, (in Racket)]] 8 | + [[https://www.coursera.org/learn/programming-languages-part-c][Programming Languages Part C, (in Ruby)]] 9 | -------------------------------------------------------------------------------- /homework/README.org: -------------------------------------------------------------------------------- 1 | 2 | | Section | Lang | Homework/Exam | Score | Point | Date | 3 | |---------+--------+---------------+---------+-------+------------------| 4 | | 0 | SML | homework0 | 100/100 | | [2016-07-24 Sun] | 5 | | 1 | SML | homework1 | 100/100 | 55/55 | [2016-08-01 Mon] | 6 | | 2 | SML | homework2 | 97/100 | 54/55 | [2016-08-11 Thu] | 7 | | 3 | SML | homework3 | 85/100 | 59/60 | [2016-08-21 Sun] | 8 | | 4 | SML | examA | 100/100 | | [2016-08-24 Wed] | 9 | | 5 | Racket | homework4 | 100/100 | 49/50 | [2016-09-10 Sat] | 10 | | 6 | Racket | homework5 | 100/100 | 70/70 | [2016-09-17 Sat] | 11 | | 7 | Racket | examB | 100/100 | | [2016-09-22 Thu] | 12 | | 8 | Ruby | homework6 | 100/100 | 40/40 | [2016-10-08 Sat] | 13 | | 9 | Ruby | homework7 | 100/100 | 45/45 | [2016-10-14 Fri] | 14 | | 10 | Ruby | examC | 100/100 | | [2016-10-20 Thu] | 15 | -------------------------------------------------------------------------------- /homework/hw0/hw0.sml: -------------------------------------------------------------------------------- 1 | (* Dan Grossman, Coursera PL, HW0 Provided Code *) 2 | 3 | (* The line below is wrong -- replacing the addition, +, with 4 | multiplication, *, will fix it *) 5 | fun f(x,y) = x * y 6 | 7 | (* Do not change these: They should be correct after fixing the code above *) 8 | 9 | fun double x = f(x,2) 10 | 11 | fun triple x = f(3,x) 12 | -------------------------------------------------------------------------------- /homework/hw0/hw0test.sml: -------------------------------------------------------------------------------- 1 | (* Homework0 Simple Test *) 2 | 3 | (* These are basic test cases. Passing these tests does not guarantee that your code will pass the actual homework grader *) 4 | (* To run the test, add a new line to the top of this file: use "homeworkname.sml"; *) 5 | (* All the tests should evaluate to true. For example, the REPL should say: val test1 = true : bool *) 6 | 7 | val test1 = double 17 = 34 8 | 9 | val test2 = double 0 = 0 10 | 11 | val test3 = triple ~4 = ~12 12 | 13 | val test4 = triple 0 = 0 14 | 15 | val test5 = f(12,27) = 324 16 | 17 | (* You can add more tests here, for example you can uncomment the line below 18 | by deleting the first two character and last two characters on the line *) 19 | 20 | val test6 = triple ~1 = ~3 21 | -------------------------------------------------------------------------------- /homework/hw1/assignment1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/homework/hw1/assignment1.pdf -------------------------------------------------------------------------------- /homework/hw1/hw1test.sml: -------------------------------------------------------------------------------- 1 | (* Homework1 Simple Test *) 2 | (* These are basic test cases. Passing these tests does not guarantee that your code will pass the actual homework grader *) 3 | (* To run the test, add a new line to the top of this file: use "homeworkname.sml"; *) 4 | (* All the tests should evaluate to true. For example, the REPL should say: val test1 = true : bool *) 5 | 6 | 7 | val test1 = is_older ((1,2,3),(2,3,4)) = true 8 | 9 | val test2 = number_in_month ([(2012,2,28),(2013,12,1)],2) = 1 10 | 11 | val test3 = number_in_months ([(2012,2,28),(2013,12,1),(2011,3,31),(2011,4,28)],[2,3,4]) = 3 12 | 13 | val test4 = dates_in_month ([(2012,2,28),(2013,12,1)],2) = [(2012,2,28)] 14 | 15 | val test5 = dates_in_months ([(2012,2,28),(2013,12,1),(2011,3,31),(2011,4,28)],[2,3,4]) = [(2012,2,28),(2011,3,31),(2011,4,28)] 16 | 17 | val test6 = get_nth (["hi", "there", "how", "are", "you"], 2) = "there" 18 | 19 | val test7 = date_to_string (2013, 6, 1) = "June 1, 2013" 20 | 21 | val test8 = number_before_reaching_sum (10, [1,2,3,4,5]) = 3 22 | 23 | val test9 = what_month 70 = 3 24 | 25 | val test10 = month_range (31, 34) = [1,2,2,2] 26 | 27 | val test11 = oldest([(2012,2,28),(2011,3,31),(2011,4,28)]) = SOME (2011,3,31) 28 | 29 | 30 | (* my challenge problems tests *) 31 | 32 | val test12_0 = remove_duplicates([2,4,2,1,2,5,7,3,5,7,9,7,8,6,4,7,2,4,2,5,0]) 33 | = [0,1,2,3,4,5,6,7,8,9] 34 | 35 | val test12_1 = number_in_months_challenge([(2012,2,28),(2013,12,1),(2011,3,31) 36 | ,(2011,4,28)],[2,3,4,2,3]) 37 | = 3 38 | 39 | val test12_2 = dates_in_months_challenge([(2012,2,28),(2013,12,1),(2011,3,31) 40 | ,(2011,4,28)],[2,3,4,2,3]) 41 | = [(2012,2,28),(2011,3,31),(2011,4,28)] 42 | 43 | val test13 = not (reasonable_date((0,2,28))) andalso 44 | not (reasonable_date((2016,13,28))) andalso 45 | not (reasonable_date((2016,2,40))) andalso 46 | reasonable_date((2016,1,1)) andalso 47 | reasonable_date((2016,12,31)) andalso 48 | reasonable_date((2016,2,28)) 49 | -------------------------------------------------------------------------------- /homework/hw2/assignment2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/homework/hw2/assignment2.pdf -------------------------------------------------------------------------------- /homework/hw2/hw2.sml: -------------------------------------------------------------------------------- 1 | (* Dan Grossman, Coursera PL, HW2 Provided Code *) 2 | 3 | (* if you use this function to compare two strings (returns true if the same 4 | string), then you avoid several of the functions in problem 1 having 5 | polymorphic types that may be confusing *) 6 | fun same_string(s1 : string, s2 : string) = 7 | s1 = s2 8 | 9 | (* put your solutions for problem 1 here *) 10 | 11 | (* 1-a *) 12 | fun all_except_option (str, strs) = 13 | let 14 | fun remove_from_list(str_to_remove, strs_remaining) = 15 | case strs_remaining of 16 | [] => [] 17 | | x::xs' => if (same_string(x, str_to_remove)) 18 | then xs' 19 | else x :: remove_from_list(str_to_remove, xs') 20 | val filtered_strs = remove_from_list(str, strs) 21 | in 22 | if filtered_strs = strs 23 | then NONE 24 | else SOME filtered_strs 25 | end 26 | 27 | 28 | (* 1-b *) 29 | fun get_substitutions1 (substitutions, str) = 30 | case substitutions of 31 | [] => [] 32 | | x::xs' => case all_except_option(str, x) of 33 | NONE => get_substitutions1(xs', str) 34 | | SOME y => y @ get_substitutions1(xs', str) 35 | 36 | 37 | (* 1-c *) 38 | fun get_substitutions2 (substitutions, str) = 39 | let fun aux (substitutions, acc) = 40 | case substitutions of 41 | [] => acc 42 | | x::xs' => case all_except_option(str, x) of 43 | NONE => aux(xs', acc) 44 | | SOME y => aux(xs', acc @ y) 45 | in 46 | aux(substitutions, []) 47 | end 48 | 49 | 50 | (* 1-d *) 51 | fun similar_names (substitutions, name) = 52 | let 53 | val {first=fst, middle=mid, last=lst} = name 54 | val similar_first_names = get_substitutions2(substitutions, fst) 55 | fun generate_names (first_names) = 56 | case first_names of 57 | [] => [] 58 | | x::xs' => {first=x, middle=mid, last=lst} :: generate_names(xs') 59 | in 60 | name :: generate_names(similar_first_names) 61 | end 62 | 63 | 64 | (* you may assume that Num is always used with values 2, 3, ..., 10 65 | though it will not really come up *) 66 | datatype suit = Clubs | Diamonds | Hearts | Spades 67 | datatype rank = Jack | Queen | King | Ace | Num of int 68 | type card = suit * rank 69 | 70 | datatype color = Red | Black 71 | datatype move = Discard of card | Draw 72 | 73 | exception IllegalMove 74 | 75 | (* put your solutions for problem 2 here *) 76 | 77 | fun same_card(c1 : card, c2 : card) = 78 | c1 = c2 79 | 80 | (* 2-a *) 81 | fun card_color (s, r) = 82 | case s of 83 | Clubs => Black 84 | | Diamonds => Red 85 | | Hearts => Red 86 | | Spades => Black 87 | 88 | 89 | (* 2-b *) 90 | fun card_value (s, r) = 91 | case r of 92 | Jack => 10 93 | | Queen => 10 94 | | King => 10 95 | | Ace => 11 96 | | Num n => n 97 | 98 | 99 | (* 2-c *) 100 | fun remove_card (cs, c, e) = 101 | case cs of 102 | [] => raise e 103 | | x::xs' => if same_card(c, x) 104 | then xs' 105 | else x :: remove_card(xs', c, e) 106 | 107 | 108 | (* 2-d *) 109 | fun all_same_color (cs) = 110 | case cs of 111 | [] => true 112 | | _::[] => true 113 | | x::y::tl => card_color(x) = card_color(y) andalso all_same_color(y::tl) 114 | 115 | 116 | (* 2-e *) 117 | fun sum_cards (cs) = 118 | let fun aux (xs, acc) = 119 | case xs of 120 | [] => acc 121 | | x::xs' => aux(xs', card_value(x) + acc) 122 | in 123 | aux(cs, 0) 124 | end 125 | 126 | 127 | (* 2-f *) 128 | fun score (hcs, goal) = 129 | let 130 | val sum = sum_cards(hcs) 131 | val preliminary_score = if sum > goal then 3 * (sum - goal) else goal - sum 132 | in 133 | if all_same_color(hcs) 134 | then preliminary_score div 2 135 | else preliminary_score 136 | end 137 | 138 | 139 | (* 2-g *) 140 | fun officiate (cards, moves, goal) = 141 | let 142 | fun aux(card_list, move_list, hold_list) = 143 | case move_list of 144 | [] => score(hold_list, goal) 145 | | (Discard c)::tl 146 | => aux(card_list, tl, remove_card(card_list, c, IllegalMove)) 147 | | Draw::tl => case card_list of 148 | [] => score(hold_list, goal) 149 | | x::xs' => if sum_cards(x::hold_list) > goal 150 | then score(x::hold_list, goal) 151 | else aux(xs', tl, x::hold_list) 152 | in 153 | aux(cards, moves, []) 154 | end 155 | -------------------------------------------------------------------------------- /homework/hw2/hw2test.sml: -------------------------------------------------------------------------------- 1 | (* Homework2 Simple Test *) 2 | (* These are basic test cases. Passing these tests does not guarantee that your code will pass the actual homework grader *) 3 | (* To run the test, add a new line to the top of this file: use "homeworkname.sml"; *) 4 | (* All the tests should evaluate to true. For example, the REPL should say: val test1 = true : bool *) 5 | 6 | val test1 = all_except_option ("string", ["string"]) = SOME [] 7 | 8 | val test2 = get_substitutions1 ([["foo"],["there"]], "foo") = [] 9 | 10 | val test3 = get_substitutions2 ([["foo"],["there"]], "foo") = [] 11 | 12 | val test4 = similar_names ([["Fred","Fredrick"],["Elizabeth","Betty"],["Freddie","Fred","F"]], {first="Fred", middle="W", last="Smith"}) = 13 | [{first="Fred", last="Smith", middle="W"}, {first="Fredrick", last="Smith", middle="W"}, 14 | {first="Freddie", last="Smith", middle="W"}, {first="F", last="Smith", middle="W"}] 15 | 16 | val test5 = card_color (Clubs, Num 2) = Black 17 | 18 | val test6 = card_value (Clubs, Num 2) = 2 19 | 20 | val test7 = remove_card ([(Hearts, Ace)], (Hearts, Ace), IllegalMove) = [] 21 | 22 | val test8 = all_same_color [(Hearts, Ace), (Hearts, Ace)] = true 23 | 24 | val test9 = sum_cards [(Clubs, Num 2),(Clubs, Num 2)] = 4 25 | 26 | val test10 = score ([(Hearts, Num 2),(Clubs, Num 4)],10) = 4 27 | 28 | val test11 = officiate ([(Hearts, Num 2),(Clubs, Num 4)],[Draw], 15) = 6 29 | 30 | val test12 = officiate ([(Clubs,Ace),(Spades,Ace),(Clubs,Ace),(Spades,Ace)], 31 | [Draw,Draw,Draw,Draw,Draw], 32 | 42) 33 | = 3 34 | 35 | val test13 = ((officiate([(Clubs,Jack),(Spades,Num(8))], 36 | [Draw,Discard(Hearts,Jack)], 37 | 42); 38 | false) 39 | handle IllegalMove => true) 40 | -------------------------------------------------------------------------------- /homework/hw3/assignment3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/homework/hw3/assignment3.pdf -------------------------------------------------------------------------------- /homework/hw3/hw3.sml: -------------------------------------------------------------------------------- 1 | (* Coursera Programming Languages, Homework 3, Provided Code *) 2 | 3 | exception NoAnswer 4 | 5 | datatype pattern = Wildcard 6 | | Variable of string 7 | | UnitP 8 | | ConstP of int 9 | | TupleP of pattern list 10 | | ConstructorP of string * pattern 11 | 12 | datatype valu = Const of int 13 | | Unit 14 | | Tuple of valu list 15 | | Constructor of string * valu 16 | 17 | fun g f1 f2 p = 18 | let 19 | val r = g f1 f2 20 | in 21 | case p of 22 | Wildcard => f1 () 23 | | Variable x => f2 x 24 | | TupleP ps => List.foldl (fn (p,i) => (r p) + i) 0 ps 25 | | ConstructorP(_,p) => r p 26 | | _ => 0 27 | end 28 | 29 | (**** for the challenge problem only ****) 30 | 31 | datatype typ = Anything 32 | | UnitT 33 | | IntT 34 | | TupleT of typ list 35 | | Datatype of string 36 | 37 | (**** you can put all your code here ****) 38 | 39 | (* 1 *) 40 | fun only_capitals str_lst = 41 | List.filter (fn str => Char.isUpper(String.sub(str, 0))) str_lst 42 | 43 | 44 | (* 2 *) 45 | fun longest_string1 str_lst = 46 | foldl (fn (x,y) => if String.size x > String.size y then x else y) "" str_lst 47 | 48 | 49 | (* 3 *) 50 | fun longest_string2 str_lst = 51 | foldl (fn (x,y) => if String.size x >= String.size y then x else y) "" str_lst 52 | 53 | 54 | (* 4 *) 55 | fun longset_string_helper compare_func str_lst = 56 | foldl (fn (x,y) => if compare_func(String.size x,String.size y) then x else y) 57 | "" 58 | str_lst 59 | 60 | val longest_string3 = longset_string_helper (fn (x,y) => x > y) 61 | 62 | val longest_string4 = longset_string_helper (fn (x,y) => x >= y) 63 | 64 | 65 | (* 5 *) 66 | val longest_capitalized = longest_string1 o only_capitals 67 | 68 | 69 | (* 6 *) 70 | val rev_string = String.implode o rev o String.explode 71 | 72 | 73 | (* 7 *) 74 | fun first_answer f lst = 75 | case lst of 76 | [] => raise NoAnswer 77 | | x::xs' => case f x of 78 | SOME v => v 79 | | NONE => first_answer f xs' 80 | 81 | 82 | (* 8 *) 83 | fun all_answers f lst = 84 | let 85 | fun aux lst acc = 86 | case lst of 87 | [] => SOME acc 88 | | x::xs' => case f x of 89 | NONE => NONE 90 | | SOME y => aux xs' (acc @ y) 91 | in 92 | aux lst [] 93 | end 94 | 95 | 96 | (* 9-a *) 97 | fun count_wildcards p = 98 | g (fn () => 1) (fn str => 0) p 99 | 100 | 101 | (* 9-b *) 102 | fun count_wild_and_variable_lengths p = 103 | g (fn () => 1) String.size p 104 | 105 | 106 | (* 9-c *) 107 | fun count_some_var (s, p) = 108 | g (fn () => 0) (fn str => if s = str then 1 else 0) p 109 | 110 | 111 | (* 10 *) 112 | fun check_pat p = 113 | let 114 | fun ptn_to_strlst ptn = 115 | case ptn of 116 | Variable s => [s] 117 | | TupleP ps => List.foldl (fn (p,i) => (ptn_to_strlst p) @ i) [] ps 118 | | ConstructorP(_,p) => ptn_to_strlst p 119 | | _ => [] 120 | fun has_repeats str_lst = 121 | case str_lst of 122 | [] => false 123 | | x::xs' => if List.exists (fn y => y = x) xs' then true else 124 | has_repeats xs' 125 | val f = not o has_repeats o ptn_to_strlst 126 | in 127 | f p 128 | end 129 | 130 | 131 | (* 11 *) 132 | fun match (v, p) = 133 | case (v, p) of 134 | (_, Wildcard) => SOME [] 135 | | (sv, Variable sp) => SOME [(sp,sv)] 136 | | (Unit, UnitP) => SOME [] 137 | | (Const iv, ConstP ip) => if iv = ip then SOME [] else NONE 138 | | (Tuple tv, TupleP tp) => if List.length tv = List.length tp 139 | then all_answers match (ListPair.zip(tv, tp)) 140 | else NONE 141 | | (Constructor (s1,cv), ConstructorP (s2,cp)) => if s1 = s2 142 | then match (cv,cp) 143 | else NONE 144 | | (_, _) => NONE 145 | 146 | 147 | (* 12 *) 148 | fun first_match v p_lst = 149 | SOME (first_answer (fn x => match(v, x)) p_lst) handle NoAnswer => NONE 150 | -------------------------------------------------------------------------------- /homework/hw3/hw3test.sml: -------------------------------------------------------------------------------- 1 | (* Homework3 Simple Test*) 2 | (* These are basic test cases. Passing these tests does not guarantee that your code will pass the actual homework grader *) 3 | (* To run the test, add a new line to the top of this file: use "homeworkname.sml"; *) 4 | (* All the tests should evaluate to true. For example, the REPL should say: val test1 = true : bool *) 5 | 6 | val test1 = only_capitals ["A","B","C"] = ["A","B","C"] 7 | 8 | val test2 = longest_string1 ["A","bc","C"] = "bc" 9 | 10 | val test3 = longest_string2 ["A","bc","C"] = "bc" 11 | 12 | val test4a = longest_string3 ["A","bc","C"] = "bc" 13 | 14 | val test4b = longest_string4 ["A","B","C"] = "C" 15 | 16 | val test5 = longest_capitalized ["A","bc","C"] = "A" 17 | 18 | val test6 = rev_string "abc" = "cba" 19 | 20 | val test7 = first_answer (fn x => if x > 3 then SOME x else NONE) [1,2,3,4,5] = 4 21 | 22 | val test8 = all_answers (fn x => if x = 1 then SOME [x] else NONE) [2,3,4,5,6,7] = NONE 23 | 24 | val test9a = count_wildcards Wildcard = 1 25 | 26 | val test9b = count_wild_and_variable_lengths (Variable("a")) = 1 27 | 28 | val test9c = count_some_var ("x", Variable("x")) = 1 29 | 30 | val test10 = check_pat (Variable("x")) = true 31 | 32 | val test11 = match (Const(1), UnitP) = NONE 33 | 34 | val test12 = first_match Unit [UnitP] = SOME [] 35 | -------------------------------------------------------------------------------- /homework/hw4/assignment4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/homework/hw4/assignment4.pdf -------------------------------------------------------------------------------- /homework/hw4/curry.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/homework/hw4/curry.jpg -------------------------------------------------------------------------------- /homework/hw4/dan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/homework/hw4/dan.jpg -------------------------------------------------------------------------------- /homework/hw4/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/homework/hw4/dog.jpg -------------------------------------------------------------------------------- /homework/hw4/dog2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/homework/hw4/dog2.jpg -------------------------------------------------------------------------------- /homework/hw4/hw4.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide (all-defined-out)) ;; so we can put tests in a second file 4 | 5 | ;; put your code below 6 | 7 | ;; (1) 8 | (define (sequence low high stride) 9 | (if (> low high) 10 | null 11 | (cons low (sequence (+ low stride) high stride)))) 12 | 13 | 14 | ;; (2) 15 | (define (string-append-map xs suffix) 16 | (map (lambda (x) (string-append x suffix)) xs)) 17 | 18 | 19 | ;; (3) 20 | (define (list-nth-mod xs n) 21 | (cond [(< n 0) (error "list-nth-mod: negative number")] 22 | [(= (length xs) 0) (error "list-nth-mode: empty list")] 23 | [#t (let ([i (remainder n (length xs))]) 24 | (car (list-tail xs i)))])) 25 | 26 | 27 | ;; (4) 28 | (define (stream-for-n-steps s n) 29 | (if (= n 0) 30 | null 31 | (let ([pr (s)]) 32 | (cons (car pr) (stream-for-n-steps (cdr pr) (- n 1)))))) 33 | 34 | 35 | ;; (5) 36 | (define funny-number-stream 37 | (letrec ([f (lambda (x) (cons (if (= (remainder x 5) 0) (- x) x) 38 | (lambda () (f (+ x 1)))))]) 39 | (lambda () (f 1)))) 40 | 41 | 42 | ;; (6) 43 | (define dan-then-dog 44 | (letrec ([f (lambda (b) 45 | (if b 46 | (cons "dan.jpg" (lambda () (f #f))) 47 | (cons "dog.jpg" (lambda () (f #t)))))]) 48 | (lambda () (f #t)))) 49 | 50 | 51 | ;; (7) 52 | (define (stream-add-zero s) 53 | (lambda () 54 | (let ([pr (s)]) 55 | (cons (cons 0 (car pr)) (stream-add-zero (cdr pr)))))) 56 | 57 | 58 | ;; (8) 59 | (define (cycle-lists xs ys) 60 | (letrec ([f (lambda (n) 61 | (let ([x (list-nth-mod xs n)] 62 | [y (list-nth-mod ys n)]) 63 | (cons (cons x y) (lambda () (f (+ n 1))))))]) 64 | (lambda () (f 0)))) 65 | 66 | 67 | ;; (9) 68 | (define (vector-assoc v vec) 69 | (letrec ([f (lambda (n) 70 | (if (>= n (vector-length vec)) 71 | #f 72 | (let ([ith (vector-ref vec n)]) 73 | (if (and (pair? ith) (equal? (car ith) v)) 74 | ith 75 | (f (+ n 1))))))]) 76 | (f 0))) 77 | 78 | 79 | ;; (10) 80 | (define (cached-assoc xs n) 81 | (letrec ([memo (make-vector n #f)] 82 | [pos 0]) 83 | (lambda (v) 84 | (or (vector-assoc v memo) 85 | (let ([new-ans (assoc v xs)]) 86 | (and new-ans 87 | (begin 88 | (vector-set! memo pos new-ans) 89 | (set! pos (remainder (+ pos 1) n)) 90 | new-ans))))))) 91 | 92 | 93 | ;; Challenge Problem 94 | ;; (11) 95 | (define-syntax while-less 96 | (syntax-rules (do) 97 | [(while-less e1 do e2) 98 | (letrec ([val-of-e1 e1] 99 | [loop (lambda () 100 | (if (>= e2 val-of-e1) 101 | #t 102 | (loop)))]) 103 | (loop))])) 104 | -------------------------------------------------------------------------------- /homework/hw4/hw4combined_test_with_graphisc.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | ;; Programming Languages Homework4 Graphical Tests 4 | ;; Save this file to the same directory as your homework file 5 | ;; These are tests that use a little graphics to make the assignment more fun. 6 | 7 | ;; Be sure to put your homework file in the same folder as this test file. 8 | ;; Uncomment the line below and change HOMEWORK_FILE to the name of your homework file. 9 | (require "hw4.rkt") 10 | 11 | ;; A simple library for displaying a 2x3 grid of pictures: used 12 | ;; for fun in the tests below (look for "Tests Start Here"). No need to understand 13 | ;; the graphics code, though it is not sophisticated. 14 | 15 | (require (lib "graphics.rkt" "graphics")) 16 | 17 | (open-graphics) 18 | 19 | (define window-name "Programming Languages, Homework 4") 20 | (define window-width 700) 21 | (define window-height 500) 22 | (define border-size 100) 23 | 24 | (define approx-pic-width 200) 25 | (define approx-pic-height 200) 26 | (define pic-grid-width 3) 27 | (define pic-grid-height 2) 28 | 29 | (define (open-window) 30 | (open-viewport window-name window-width window-height)) 31 | 32 | (define (grid-posn-to-posn grid-posn) 33 | (when (>= grid-posn (* pic-grid-height pic-grid-width)) 34 | (error "picture grid does not have that many positions")) 35 | (let ([row (quotient grid-posn pic-grid-width)] 36 | [col (remainder grid-posn pic-grid-width)]) 37 | (make-posn (+ border-size (* approx-pic-width col)) 38 | (+ border-size (* approx-pic-height row))))) 39 | 40 | (define (place-picture window filename grid-posn) 41 | (let ([posn (grid-posn-to-posn grid-posn)]) 42 | ((clear-solid-rectangle window) posn approx-pic-width approx-pic-height) 43 | ((draw-pixmap window) filename posn))) 44 | 45 | (define (place-repeatedly window pause stream n) 46 | (when (> n 0) 47 | (let* ([next (stream)] 48 | [filename (cdar next)] 49 | [grid-posn (caar next)] 50 | [stream (cdr next)]) 51 | (place-picture window filename grid-posn) 52 | (sleep pause) 53 | (place-repeatedly window pause stream (- n 1))))) 54 | 55 | ;; Tests Start Here 56 | 57 | ; These definitions will work only after you do some of the problems 58 | ; so you need to comment them out until you are ready. 59 | ; Add more tests as appropriate, of course. 60 | 61 | (define nums (sequence 0 5 1)) 62 | 63 | (define files (string-append-map 64 | (list "dan" "dog" "curry" "dog2") 65 | ".jpg")) 66 | 67 | ; a zero-argument function: call (one-visual-test) to open the graphics window, etc. 68 | (define (one-visual-test) 69 | (place-repeatedly (open-window) 0.5 (cycle-lists nums files) 27)) 70 | 71 | ; similar to previous but uses only two files and one position on the grid 72 | (define (visual-zero-only) 73 | (place-repeatedly (open-window) 0.5 (stream-add-zero dan-then-dog) 27)) 74 | -------------------------------------------------------------------------------- /homework/hw4/hw4test.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | ;; Programming Languages Homework4 Simple Test 3 | ;; Save this file to the same directory as your homework file 4 | ;; These are basic tests. Passing these tests does not guarantee that your code will pass the actual homework grader 5 | 6 | ;; Be sure to put your homework file in the same folder as this test file. 7 | ;; Uncomment the line below and change HOMEWORK_FILE to the name of your homework file. 8 | ;;(require "HOMEWORK_FILE") 9 | (require "hw4.rkt") 10 | (require rackunit) 11 | 12 | ;; Helper functions 13 | (define ones (lambda () (cons 1 ones))) 14 | (define a 2) 15 | 16 | (define tests 17 | (test-suite 18 | "Sample tests for Assignment 4" 19 | 20 | ; sequence test 21 | (check-equal? (sequence 0 5 1) (list 0 1 2 3 4 5) "Sequence test") 22 | 23 | ; string-append-map test 24 | (check-equal? (string-append-map 25 | (list "dan" "dog" "curry" "dog2") 26 | ".jpg") '("dan.jpg" "dog.jpg" "curry.jpg" "dog2.jpg") "string-append-map test") 27 | 28 | ; list-nth-mod test 29 | (check-equal? (list-nth-mod (list 0 1 2 3 4) 2) 2 "list-nth-mod test") 30 | 31 | ; stream-for-n-steps test 32 | (check-equal? (stream-for-n-steps (lambda () (cons 1 ones)) 1) (list 1) "stream-for-n-steps test") 33 | 34 | ; funny-number-stream test 35 | (check-equal? (stream-for-n-steps funny-number-stream 16) (list 1 2 3 4 -5 6 7 8 9 -10 11 12 13 14 -15 16) "funny-number-stream test") 36 | 37 | ; dan-then-dog test 38 | (check-equal? (stream-for-n-steps dan-then-dog 1) (list "dan.jpg") "dan-then-dog test") 39 | 40 | ; stream-add-zero test 41 | (check-equal? (stream-for-n-steps (stream-add-zero ones) 1) (list (cons 0 1)) "stream-add-zero test") 42 | 43 | ; cycle-lists test 44 | (check-equal? (stream-for-n-steps (cycle-lists (list 1 2 3) (list "a" "b")) 3) (list (cons 1 "a") (cons 2 "b") (cons 3 "a")) 45 | "cycle-lists test") 46 | 47 | ; vector-assoc test 48 | (check-equal? (vector-assoc 4 (vector (cons 2 1) (cons 3 1) (cons 4 1) (cons 5 1))) (cons 4 1) "vector-assoc test") 49 | 50 | ; cached-assoc tests 51 | (check-equal? ((cached-assoc (list (cons 1 2) (cons 3 4)) 3) 3) (cons 3 4) "cached-assoc test") 52 | 53 | ; while-less test 54 | (check-equal? (while-less 7 do (begin (set! a (+ a 1)) a)) #t "while-less test") 55 | 56 | )) 57 | 58 | (require rackunit/text-ui) 59 | ;; runs the test 60 | (run-tests tests) 61 | -------------------------------------------------------------------------------- /homework/hw5/assignment5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/homework/hw5/assignment5.pdf -------------------------------------------------------------------------------- /homework/hw5/hw5test.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | ;; Programming Languages Homework 5 Simple Test 3 | ;; Save this file to the same directory as your homework file 4 | ;; These are basic tests. Passing these tests does not guarantee that your code will pass the actual homework grader 5 | 6 | ;; Be sure to put your homework file in the same folder as this test file. 7 | ;; Uncomment the line below and, if necessary, change the filename 8 | (require "hw5.rkt") 9 | 10 | (require rackunit) 11 | 12 | (define tests 13 | (test-suite 14 | "Sample tests for Assignment 5" 15 | 16 | ;; check racketlist to mupllist with normal list 17 | (check-equal? (racketlist->mupllist (list (int 3) (int 4))) (apair (int 3) (apair (int 4) (aunit))) "racketlist->mupllist test") 18 | 19 | ;; check mupllist to racketlist with normal list 20 | (check-equal? (mupllist->racketlist (apair (int 3) (apair (int 4) (aunit)))) (list (int 3) (int 4)) "racketlist->mupllist test") 21 | 22 | ;; tests if ifgreater returns (int 2) 23 | (check-equal? (eval-exp (ifgreater (int 3) (int 4) (int 3) (int 2))) (int 2) "ifgreater test") 24 | 25 | ;; mlet test 26 | (check-equal? (eval-exp (mlet "x" (int 1) (add (int 5) (var "x")))) (int 6) "mlet test") 27 | 28 | ;; call test 29 | (check-equal? (eval-exp (call (closure '() (fun #f "x" (add (var "x") (int 7)))) (int 1))) (int 8) "call test") 30 | 31 | ;;snd test 32 | (check-equal? (eval-exp (snd (apair (int 1) (int 2)))) (int 2) "snd test") 33 | 34 | ;; isaunit test 35 | (check-equal? (eval-exp (isaunit (closure '() (fun #f "x" (aunit))))) (int 0) "isaunit test") 36 | 37 | ;; ifaunit test 38 | (check-equal? (eval-exp (ifaunit (int 1) (int 2) (int 3))) (int 3) "ifaunit test") 39 | 40 | ;; mlet* test 41 | (check-equal? (eval-exp (mlet* (list (cons "x" (int 10))) (var "x"))) (int 10) "mlet* test") 42 | 43 | ;; ifeq test 44 | (check-equal? (eval-exp (ifeq (int 1) (int 2) (int 3) (int 4))) (int 4) "ifeq test") 45 | 46 | ;; mupl-map test 47 | (check-equal? (eval-exp (call (call mupl-map (fun #f "x" (add (var "x") (int 7)))) (apair (int 1) (aunit)))) 48 | (apair (int 8) (aunit)) "mupl-map test") 49 | 50 | ;; problems 1, 2, and 4 combined test 51 | (check-equal? (mupllist->racketlist 52 | (eval-exp (call (call mupl-mapAddN (int 7)) 53 | (racketlist->mupllist 54 | (list (int 3) (int 4) (int 9)))))) (list (int 10) (int 11) (int 16)) "combined test") 55 | 56 | )) 57 | 58 | (require rackunit/text-ui) 59 | ;; runs the test 60 | (run-tests tests) 61 | -------------------------------------------------------------------------------- /homework/hw6/assignment6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/homework/hw6/assignment6.pdf -------------------------------------------------------------------------------- /homework/hw6/hw6assignment.rb: -------------------------------------------------------------------------------- 1 | # University of Washington, Programming Languages, Homework 6, hw6runner.rb 2 | 3 | # This is the only file you turn in, so do not modify the other files as 4 | # part of your solution. 5 | 6 | class MyPiece < Piece 7 | # The constant All_My_Pieces should be declared here 8 | 9 | All_My_Pieces = [rotations([[0, 0], [-1, 0], [-1, -1], [0, -1], [1, 0]]), 10 | [[[0, 0], [-1, 0], [-2, 0], [1, 0], [2, 0]], 11 | [[0, 0], [0, -1], [0, -2], [0, 1], [0, 2]]], 12 | rotations([[0, 0], [0, -1], [1, 0]])] + All_Pieces 13 | 14 | # your enhancements here 15 | 16 | def self.next_piece (board) 17 | MyPiece.new(All_My_Pieces.sample, board) 18 | end 19 | 20 | def self.next_cheat_piece (board) 21 | MyPiece.new([[[0, 0]]], board) 22 | end 23 | end 24 | 25 | class MyBoard < Board 26 | # your enhancements here 27 | 28 | def initialize (game) 29 | @cheat = false 30 | @grid = Array.new(num_rows) {Array.new(num_columns)} 31 | @current_block = MyPiece.next_piece(self) 32 | @score = 0 33 | @game = game 34 | @delay = 500 35 | end 36 | 37 | def rotate_180 38 | if !game_over? and @game.is_running? 39 | @current_block.move(0, 0, 2) 40 | end 41 | draw 42 | end 43 | 44 | def cheat_next 45 | if @score >= 100 and !@cheat 46 | @score -= 100 47 | @cheat = true 48 | end 49 | end 50 | 51 | def next_piece 52 | if @cheat 53 | @current_block = MyPiece.next_cheat_piece(self) 54 | @cheat = false 55 | else 56 | @current_block = MyPiece.next_piece(self) 57 | end 58 | @current_pos = nil 59 | end 60 | 61 | def store_current 62 | locations = @current_block.current_rotation 63 | displacement = @current_block.position 64 | (0..(locations.size - 1)).each{|index| 65 | current = locations[index] 66 | @grid[current[1]+displacement[1]][current[0]+displacement[0]] = 67 | @current_pos[index] 68 | } 69 | remove_filled 70 | @delay = [@delay - 2, 80].max 71 | end 72 | end 73 | 74 | class MyTetris < Tetris 75 | # your enhancements here 76 | 77 | def set_board 78 | @canvas = TetrisCanvas.new 79 | @board = MyBoard.new(self) 80 | @canvas.place(@board.block_size * @board.num_rows + 3, 81 | @board.block_size * @board.num_columns + 6, 24, 80) 82 | @board.draw 83 | end 84 | 85 | def key_bindings 86 | super 87 | @root.bind('c', proc {@board.cheat_next}) 88 | @root.bind('u', proc {@board.rotate_180}) 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /homework/hw6/hw6graphics.rb: -------------------------------------------------------------------------------- 1 | # University of Washington, Programming Languages, Homework 6, hw6graphics.rb 2 | 3 | # This file provides an interface to a wrapped Tk library. The auto-grader will 4 | # swap it out to use a different, non-Tk backend. 5 | 6 | require 'tk' 7 | 8 | class TetrisRoot 9 | def initialize 10 | @root = TkRoot.new('height' => 615, 'width' => 205, 11 | 'background' => 'lightblue') {title "Tetris"} 12 | end 13 | 14 | def bind(char, callback) 15 | @root.bind(char, callback) 16 | end 17 | 18 | # Necessary so we can unwrap before passing to Tk in some instances. 19 | # Student code MUST NOT CALL THIS. 20 | attr_reader :root 21 | end 22 | 23 | class TetrisTimer 24 | def initialize 25 | @timer = TkTimer.new 26 | end 27 | 28 | def stop 29 | @timer.stop 30 | end 31 | 32 | def start(delay, callback) 33 | @timer.start(delay, callback) 34 | end 35 | end 36 | 37 | class TetrisCanvas 38 | def initialize 39 | @canvas = TkCanvas.new('background' => 'grey') 40 | end 41 | 42 | def place(height, width, x, y) 43 | @canvas.place('height' => height, 'width' => width, 'x' => x, 'y' => y) 44 | end 45 | 46 | def unplace 47 | @canvas.unplace 48 | end 49 | 50 | def delete 51 | @canvas.delete 52 | end 53 | 54 | # Necessary so we can unwrap before passing to Tk in some instances. 55 | # Student code MUST NOT CALL THIS. 56 | attr_reader :canvas 57 | end 58 | 59 | class TetrisLabel 60 | def initialize(wrapped_root, &options) 61 | unwrapped_root = wrapped_root.root 62 | @label = TkLabel.new(unwrapped_root, &options) 63 | end 64 | 65 | def place(height, width, x, y) 66 | @label.place('height' => height, 'width' => width, 'x' => x, 'y' => y) 67 | end 68 | 69 | def text(str) 70 | @label.text(str) 71 | end 72 | end 73 | 74 | class TetrisButton 75 | def initialize(label, color) 76 | @button = TkButton.new do 77 | text label 78 | background color 79 | command (proc {yield}) 80 | end 81 | end 82 | 83 | def place(height, width, x, y) 84 | @button.place('height' => height, 'width' => width, 'x' => x, 'y' => y) 85 | end 86 | end 87 | 88 | class TetrisRect 89 | def initialize(wrapped_canvas, a, b, c, d, color) 90 | unwrapped_canvas = wrapped_canvas.canvas 91 | @rect = TkcRectangle.new(unwrapped_canvas, a, b, c, d, 92 | 'outline' => 'black', 'fill' => color) 93 | end 94 | 95 | def remove 96 | @rect.remove 97 | end 98 | 99 | def move(dx, dy) 100 | @rect.move(dx, dy) 101 | end 102 | 103 | end 104 | 105 | def mainLoop 106 | Tk.mainloop 107 | end 108 | 109 | def exitProgram 110 | Tk.exit 111 | end 112 | -------------------------------------------------------------------------------- /homework/hw6/hw6runner.rb: -------------------------------------------------------------------------------- 1 | # University of Washington, Programming Languages, Homework 6, hw6runner.rb 2 | 3 | require_relative './hw6provided' 4 | require_relative './hw6assignment' 5 | 6 | def runTetris 7 | Tetris.new 8 | mainLoop 9 | end 10 | 11 | def runMyTetris 12 | MyTetris.new 13 | mainLoop 14 | end 15 | 16 | if ARGV.count == 0 17 | runMyTetris 18 | elsif ARGV.count != 1 19 | puts "usage: hw6runner.rb [enhanced | original]" 20 | elsif ARGV[0] == "enhanced" 21 | runMyTetris 22 | elsif ARGV[0] == "original" 23 | runTetris 24 | else 25 | puts "usage: hw6runner.rb [enhanced | original]" 26 | end 27 | -------------------------------------------------------------------------------- /homework/hw7/assignment7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/homework/hw7/assignment7.pdf -------------------------------------------------------------------------------- /homework/hw7/hw7testprovided.sml: -------------------------------------------------------------------------------- 1 | (* University of Washington, Programming Languages, Homework 7 2 | hw7testsprovided.sml *) 3 | (* Will not compile until you implement preprocess and eval_prog *) 4 | 5 | (* These tests do NOT cover all the various cases, especially for intersection *) 6 | 7 | use "hw7.sml"; 8 | 9 | (* Must implement preprocess_prog and Shift before running these tests *) 10 | 11 | fun real_equal(x,y) = Real.compare(x,y) = General.EQUAL; 12 | 13 | (* Preprocess tests *) 14 | let 15 | val Point(a,b) = preprocess_prog(LineSegment(3.2,4.1,3.2,4.1)) 16 | val Point(c,d) = Point(3.2,4.1) 17 | in 18 | if real_equal(a,c) andalso real_equal(b,d) 19 | then (print "preprocess converts a LineSegment to a Point successfully\n") 20 | else (print "preprocess does not convert a LineSegment to a Point succesfully\n") 21 | end; 22 | 23 | let 24 | val LineSegment(a,b,c,d) = preprocess_prog (LineSegment(3.2,4.1,~3.2,~4.1)) 25 | val LineSegment(e,f,g,h) = LineSegment(~3.2,~4.1,3.2,4.1) 26 | in 27 | if real_equal(a,e) andalso real_equal(b,f) andalso real_equal(c,g) andalso real_equal(d,h) 28 | then (print "preprocess flips an improper LineSegment successfully\n") 29 | else (print "preprocess does not flip an improper LineSegment successfully\n") 30 | end; 31 | 32 | (* eval_prog tests with Shift*) 33 | let 34 | val Point(a,b) = (eval_prog (preprocess_prog (Shift(3.0, 4.0, Point(4.0,4.0))), [])) 35 | val Point(c,d) = Point(7.0,8.0) 36 | in 37 | if real_equal(a,c) andalso real_equal(b,d) 38 | then (print "eval_prog with empty environment worked\n") 39 | else (print "eval_prog with empty environment is not working properly\n") 40 | end; 41 | 42 | (* Using a Var *) 43 | let 44 | val Point(a,b) = (eval_prog (Shift(3.0,4.0,Var "a"), [("a",Point(4.0,4.0))])) 45 | val Point(c,d) = Point(7.0,8.0) 46 | in 47 | if real_equal(a,c) andalso real_equal(b,d) 48 | then (print "eval_prog with 'a' in environment is working properly\n") 49 | else (print "eval_prog with 'a' in environment is not working properly\n") 50 | end; 51 | 52 | 53 | (* With Variable Shadowing *) 54 | let 55 | val Point(a,b) = (eval_prog (Shift(3.0,4.0,Var "a"), [("a",Point(4.0,4.0)),("a",Point(1.0,1.0))])) 56 | val Point(c,d) = Point(7.0,8.0) 57 | in 58 | if real_equal(a,c) andalso real_equal(b,d) 59 | then (print "eval_prog with shadowing 'a' in environment is working properly\n") 60 | else (print "eval_prog with shadowing 'a' in environment is not working properly\n") 61 | end; 62 | -------------------------------------------------------------------------------- /resources/01code/10_repl.txt: -------------------------------------------------------------------------------- 1 | - []; 2 | val it = [] : 'a list 3 | - [3,4,5]; 4 | val it = [3,4,5] : int list 5 | - [4,3]; 6 | val it = [4,3] : int list 7 | - [3,4,5,6]; 8 | val it = [3,4,5,6] : int list 9 | - [(1+2),3+4,7]; 10 | val it = [3,7,7] : int list 11 | - [true,false,true]; 12 | val it = [true,false,true] : bool list 13 | - [3,4+5,true]; 14 | stdIn:7.1-7.13 Error: operator and operand don't agree [literal] 15 | operator domain: int * int list 16 | operand: int * bool list 17 | in expression: 18 | 4 + 5 :: true :: nil 19 | - 4+true; 20 | stdIn:1.2-2.3 Error: operator and operand don't agree [literal] 21 | operator domain: int * int 22 | operand: int * bool 23 | in expression: 24 | 4 + true 25 | - val x = [7,8,9]; 26 | val x = [7,8,9] : int list 27 | - x; 28 | val it = [7,8,9] : int list 29 | - 5::x; 30 | val it = [5,7,8,9] : int list 31 | - 6::5::x; 32 | val it = [6,5,7,8,9] : int list 33 | - [6]::x; 34 | stdIn:11.1-11.7 Error: operator and operand don't agree [tycon mismatch] 35 | operator domain: int list * int list list 36 | operand: int list * int list 37 | in expression: 38 | (6 :: nil) :: x 39 | - 6::x; 40 | val it = [6,7,8,9] : int list 41 | - [6]::[[7,5],[5,2]]; 42 | val it = [[6],[7,5],[5,2]] : int list list 43 | - null x; 44 | val it = false : bool 45 | - x; 46 | val it = [7,8,9] : int list 47 | - null []; 48 | val it = true : bool 49 | - val y = []; 50 | val y = [] : 'a list 51 | - null y; 52 | val it = true : bool 53 | - hd x; 54 | val it = 7 : int 55 | - x; 56 | val it = [7,8,9] : int list 57 | - tl x; 58 | val it = [8,9] : int list 59 | - hd (tl x); 60 | val it = 8 : int 61 | - tl (tl x); 62 | val it = [9] : int list 63 | - tl (tl (tl x)); 64 | val it = [] : int list 65 | - tl (tl (tl (tl x))); 66 | 67 | uncaught exception Empty 68 | raised at: smlnj/init/pervasive.sml:211.19-211.24 69 | - hd (tl (tl (tl x))); 70 | 71 | uncaught exception Empty 72 | raised at: smlnj/init/pervasive.sml:209.19-209.24 73 | - [(3,4),(5,6)]; 74 | val it = [(3,4),(5,6)] : (int * int) list 75 | - 3::it; 76 | stdIn:25.1-25.6 Error: operator and operand don't agree [literal] 77 | operator domain: int * int list 78 | operand: int * (int * int) list 79 | in expression: 80 | 3 :: it 81 | - (1,2)::it; 82 | val it = [(1,2),(3,4),(5,6)] : (int * int) list 83 | - []; 84 | val it = [] : 'a list 85 | - 3::[]; 86 | val it = [3] : int list 87 | - true::[]; 88 | val it = [true] : bool list 89 | - null; 90 | val it = fn : 'a list -> bool 91 | - null [3,4]; 92 | val it = false : bool 93 | - null [true,false]; 94 | val it = false : bool 95 | - hd; 96 | val it = fn : 'a list -> 'a 97 | - hd [3,4]; 98 | val it = 3 : int 99 | - hd [true,false]; 100 | val it = true : bool 101 | - tl; 102 | val it = fn : 'a list -> 'a list 103 | - tl [3,4]; 104 | val it = [4] : int list 105 | 106 | -------------------------------------------------------------------------------- /resources/01code/11_list_functions.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 1: List Functions *) 3 | 4 | (* Functions taking or producing lists *) 5 | 6 | fun sum_list (xs : int list) = 7 | if null xs 8 | then 0 9 | else hd(xs) + sum_list(tl(xs)) 10 | 11 | fun countdown (x : int) = 12 | if x=0 13 | then [] 14 | else x :: countdown(x-1) 15 | 16 | fun append (xs : int list, ys : int list) = (* part of the course logo :) *) 17 | if null xs 18 | then ys 19 | else hd(xs) :: append(tl(xs), ys) 20 | 21 | (* More functions over lists, here lists of pairs of ints *) 22 | 23 | fun sum_pair_list (xs : (int * int) list) = 24 | if null xs 25 | then 0 26 | else #1 (hd(xs)) + #2 (hd(xs)) + sum_pair_list(tl(xs)) 27 | 28 | fun firsts (xs : (int * int) list) = 29 | if null xs 30 | then [] 31 | else (#1 (hd xs))::(firsts(tl xs)) 32 | 33 | fun seconds (xs : (int * int) list) = 34 | if null xs 35 | then [] 36 | else (#2 (hd xs))::(seconds(tl xs)) 37 | 38 | fun sum_pair_list2 (xs : (int * int) list) = 39 | (sum_list (firsts xs)) + (sum_list (seconds xs)) 40 | 41 | -------------------------------------------------------------------------------- /resources/01code/12_let_expressions.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 1: Let Expressions *) 3 | 4 | fun silly1 (z : int) = 5 | let val x = if z > 0 then z else 34 6 | val y = x+z+9 7 | in 8 | if x > y then x*2 else y*y 9 | end 10 | 11 | fun silly2 () = 12 | let val x = 1 13 | in 14 | (let val x = 2 in x+1 end) + (let val y = x+2 in y+1 end) 15 | end 16 | 17 | -------------------------------------------------------------------------------- /resources/01code/13_nested_functions.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 1: Nested Functions *) 3 | 4 | fun countup_from1 (x : int) = 5 | let fun count (from:int, to:int) = 6 | if from=to 7 | then to::[] (* note: can also write [to] *) 8 | else from :: count(from+1,to) 9 | in 10 | count(1,x) 11 | end 12 | 13 | fun countup_from1_better (x : int) = 14 | let fun count (from:int) = 15 | if from=x 16 | then x::[] 17 | else from :: count(from+1) 18 | in 19 | count 1 20 | end 21 | -------------------------------------------------------------------------------- /resources/01code/14_let_efficiency.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 1: Let Expressions to Avoid Repeated Computation *) 3 | 4 | (* badly named: evaluates to 0 on empty list *) 5 | fun bad_max (xs : int list) = 6 | if null xs 7 | then 0 8 | else if null (tl xs) 9 | then hd xs 10 | else if hd xs > bad_max(tl xs) 11 | then hd xs 12 | else bad_max(tl xs) 13 | 14 | (* badly named: evaluates to 0 on empty list *) 15 | fun good_max (xs : int list) = 16 | if null xs 17 | then 0 18 | else if null (tl xs) 19 | then hd xs 20 | else 21 | (* for style, could also use a let-binding for (hd xs) *) 22 | let val tl_ans = good_max(tl xs) 23 | in 24 | if hd xs > tl_ans 25 | then hd xs 26 | else tl_ans 27 | end 28 | 29 | fun countup(from : int, to : int) = 30 | if from=to 31 | then to::[] 32 | else from :: countup(from+1,to) 33 | 34 | fun countdown(from : int, to : int) = 35 | if from=to 36 | then to::[] 37 | else from :: countdown(from-1,to) 38 | 39 | -------------------------------------------------------------------------------- /resources/01code/15_options.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 1: Options *) 3 | 4 | (* badly named: evaluates to 0 on empty list *) 5 | fun old_max (xs : int list) = 6 | if null xs 7 | then 0 8 | else if null (tl xs) 9 | then hd xs 10 | else 11 | let val tl_ans = old_max(tl xs) 12 | in 13 | if hd xs > tl_ans 14 | then hd xs 15 | else tl_ans 16 | end 17 | 18 | (* better: returns an int option *) 19 | fun max1 (xs : int list) = 20 | if null xs 21 | then NONE 22 | else 23 | let val tl_ans = max1(tl xs) 24 | in if isSome tl_ans andalso valOf tl_ans > hd xs 25 | then tl_ans 26 | else SOME (hd xs) 27 | end 28 | 29 | (* looks the same as max1 to clients; 30 | implementation avoids valOf *) 31 | fun max2 (xs : int list) = 32 | if null xs 33 | then NONE 34 | else let (* fine to assume argument nonempty because it is local *) 35 | fun max_nonempty (xs : int list) = 36 | if null (tl xs) (* xs better not be [] *) 37 | then hd xs 38 | else let val tl_ans = max_nonempty(tl xs) 39 | in 40 | if hd xs > tl_ans 41 | then hd xs 42 | else tl_ans 43 | end 44 | in 45 | SOME (max_nonempty xs) 46 | end 47 | -------------------------------------------------------------------------------- /resources/01code/3_our_first_program.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 1: Our first ML program *) 3 | 4 | (* val is a keyword 5 | x is a variable name 6 | = is used as a keyword here (has different meaning in expressions) 7 | 34 is a very simple expression (and value) 8 | ; is used as a keyword here (has different meaning in expressions) 9 | *) 10 | val x = 34; 11 | (* static environment: x-->int *) 12 | (* dynamic environment: x-->34 *) 13 | 14 | val y = 17; 15 | (* static environment: y-->int, x-->int *) 16 | (* dynamic environment: y-->17, x-->34 *) 17 | 18 | (* to evaluate an addition, evaluate the subexpressions and add *) 19 | (* to evaluate a variable, lookup its value in the environment *) 20 | 21 | val z = (x + y) + (y + 2); 22 | (* static environment: z-->int, y-->int, x-->int *) 23 | (* dynamic environment: z-->70, y-->17, x-->34 *) 24 | 25 | val q = z+1; 26 | (* static environment: q-->int, z-->int, y-->int, x-->int *) 27 | (* dynamic environment: q-->71, z-->70, y-->17, x-->34 *) 28 | 29 | val abs_of_z = if z < 0 then 0 - z else z; 30 | (* static environment: abs_of_z-->int, q-->int, z-->int, y-->int, x-->int *) 31 | (* dynamic environment: abs_of_z-->70, q-->71, z-->70, y-->17, x-->34 *) 32 | 33 | val abs_of_z_simpler = abs z; 34 | -------------------------------------------------------------------------------- /resources/01code/5_errors_fixed.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 1: Some Errors *) 3 | 4 | (* This program has several errors in it so we can try to debug them. *) 5 | 6 | val x = 34 7 | 8 | val y = x + 1 9 | 10 | val z = if y > 0 then false else x < 4 11 | 12 | val q = if y > 0 then 0 else 42 13 | 14 | val a = ~5 15 | 16 | val w = 0 17 | 18 | val funny = 34 19 | 20 | val v = x div (w + 1) 21 | 22 | val fourteen = 7 + 7 23 | 24 | -------------------------------------------------------------------------------- /resources/01code/5_some_errors.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 1: Some Errors *) 3 | 4 | (* This program has several errors in it so we can try to debug them. *) 5 | 6 | val x = 34 7 | 8 | y = x + 1 9 | 10 | val z = if y then 34 else x < 4 11 | 12 | val q = if y > 0 then 0 13 | 14 | val a = -5 15 | 16 | val w = 0 17 | 18 | val fun = 34 19 | 20 | val v = x / w 21 | 22 | val fourteen = 7 - 7 23 | 24 | -------------------------------------------------------------------------------- /resources/01code/6_shadowing.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 1: Examples to Demonstrate Shadowing *) 3 | 4 | val a = 10 5 | 6 | val b = a * 2 7 | 8 | val a = 5 9 | 10 | val c = b 11 | 12 | val d = a 13 | 14 | val a = a + 1 15 | 16 | (* next line does not type-check, f not in environment *) 17 | (* val g = f - 3 *) 18 | 19 | val f = a * 2 20 | 21 | -------------------------------------------------------------------------------- /resources/01code/7_functions.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 1: simple functions *) 3 | 4 | (* this function correct only for y >= 0 *) 5 | fun pow (x:int, y:int) = 6 | if y=0 7 | then 1 8 | else x * pow(x,y-1) 9 | 10 | fun cube (x:int) = 11 | pow(x,3) 12 | 13 | val sixtyfour = cube(4) 14 | 15 | val fortytwo = pow(2,2+2) + pow(4,2) + cube(2) + 2 16 | -------------------------------------------------------------------------------- /resources/01code/9_tuples.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 1: Pairs and Tuples *) 3 | 4 | (* pairs *) 5 | 6 | fun swap (pr : int*bool) = 7 | (#2 pr, #1 pr) 8 | 9 | fun sum_two_pairs (pr1 : int*int, pr2 : int*int) = 10 | (#1 pr1) + (#2 pr1) + (#1 pr2) + (#2 pr2) 11 | 12 | (* returning a pair a real pain in Java *) 13 | fun div_mod (x : int, y : int) = 14 | (x div y, x mod y) 15 | 16 | fun sort_pair (pr : int*int) = 17 | if (#1 pr) < (#2 pr) 18 | then pr 19 | else (#2 pr, #1 pr) 20 | 21 | (* nested pairs *) 22 | 23 | val x1 = (7,(true,9)) (* int * (bool*int) *) 24 | 25 | val x2 = #1 (#2 x1) (* bool *) 26 | 27 | val x3 = (#2 x1) (* bool*int *) 28 | 29 | val x4 = ((3,5),((4,8),(0,0))) (* (int * int) * ((int * int) * (int * int)) *) 30 | -------------------------------------------------------------------------------- /resources/01summary.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/resources/01summary.pdf -------------------------------------------------------------------------------- /resources/02code/21_records.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Records *) 3 | 4 | val x = {bar = (1+2,true andalso true), foo = 3+4, baz = (false,9) } 5 | 6 | val my_niece = {name = "Amelia", id = 41123 - 12} 7 | 8 | val brain_part = {id = true, ego = false, superego = false} 9 | -------------------------------------------------------------------------------- /resources/02code/22_tuples_as_syntactic_sugar.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Tuples as Syntactic Sugar *) 3 | 4 | (* records are like tuples with user-defined field names 5 | conversely, tuples are just records with names 1, 2, ..., n 6 | the only real difference is "by position" vs. "by name" 7 | *) 8 | val a_pair = (3+1,4+2) 9 | val a_record = {second=4+2, first=3+1} 10 | 11 | (* actually, tuples *are* just records with names 1, 2, ..., n and 12 | special "by position" syntax -- our first example of "syntactic sugar" *) 13 | val another_pair = {2=5, 1=6} 14 | val sum = #1 a_pair + #2 another_pair 15 | 16 | val x = {3="hi", 1=true}; 17 | val y = {3="hi", 2=3+2, 1=true}; 18 | 19 | -------------------------------------------------------------------------------- /resources/02code/23_datatype_bindings.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Datatype Bindings *) 3 | 4 | datatype mytype = TwoInts of int * int 5 | | Str of string 6 | | Pizza 7 | 8 | val a = Str "hi" 9 | val b = Str 10 | val c = Pizza 11 | val d = TwoInts(1+2,3+4) 12 | val e = a 13 | 14 | (* Do _not_ redo datatype bindings (e.g., via use "filename.sml". 15 | Doing so will shadow the type name and the constructors.) 16 | datatype mytype = TwoInts of int * int | Str of string | Pizza *) 17 | -------------------------------------------------------------------------------- /resources/02code/24_case_expressions.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Case Expressions *) 3 | 4 | datatype mytype = TwoInts of int * int 5 | | Str of string 6 | | Pizza 7 | 8 | fun f x = 9 | case x of 10 | Pizza => 3 11 | | Str s => 8 12 | | TwoInts(i1,i2) => i1 + i2 13 | 14 | (* | Pizza => 4; (* redundant case: error *) *) 15 | (*fun g x = case x of Pizza => 3 (* missing cases: warning *) *) 16 | 17 | -------------------------------------------------------------------------------- /resources/02code/25_useful_datatypes.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Useful Datatypes *) 3 | 4 | datatype suit = Club | Diamond | Heart | Spade 5 | 6 | datatype rank = Jack | Queen | King | Ace | Num of int 7 | 8 | datatype id = StudentNum of int 9 | | Name of string * (string option) * string 10 | 11 | datatype exp = Constant of int 12 | | Negate of exp 13 | | Add of exp * exp 14 | | Multiply of exp * exp 15 | 16 | fun eval e = 17 | case e of 18 | Constant i => i 19 | | Negate e2 => ~ (eval e2) 20 | | Add(e1,e2) => (eval e1) + (eval e2) 21 | | Multiply(e1,e2) => (eval e1) * (eval e2) 22 | 23 | fun number_of_adds e = 24 | case e of 25 | Constant i => 0 26 | | Negate e2 => number_of_adds e2 27 | | Add(e1,e2) => 1 + number_of_adds e1 + number_of_adds e2 28 | | Multiply(e1,e2) => number_of_adds e1 + number_of_adds e2 29 | 30 | val example_exp = Add (Constant 19, Negate (Constant 4)) 31 | 32 | val example_ans = eval example_exp 33 | 34 | val example_addcount = number_of_adds (Multiply(example_exp,example_exp)) 35 | -------------------------------------------------------------------------------- /resources/02code/27_type_synonyms.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Type Synonyms *) 3 | 4 | datatype suit = Club | Diamond | Heart | Spade 5 | 6 | datatype rank = Jack | Queen | King | Ace | Num of int 7 | 8 | type card = suit * rank 9 | 10 | type name_record = { student_num : int option, 11 | first : string, 12 | middle : string option, 13 | last : string } 14 | 15 | fun is_Queen_of_Spades (c : card) = 16 | #1 c = Spade andalso #2 c = Queen 17 | 18 | val c1 : card = (Diamond,Ace) 19 | val c2 : suit * rank = (Heart,Ace) 20 | val c3 = (Spade,Ace) 21 | 22 | (* can call is_Queen_of_Spades with any of c1, c2, c3 *) 23 | 24 | (* and once we learn more pattern-matching, we can leave the type off 25 | function arguments too -- here is a teaser we cannot explain quite yet *) 26 | fun is_Queen_of_Spades2 c = 27 | case c of 28 | (Spade,Queen) => true 29 | | _ => false 30 | 31 | 32 | (* REPL prints 33 | val is_Queen_of_Spades2 = fn : suit * rank -> bool 34 | but that is also 35 | val is_Queen_of_Spades2 = fn : card -> bool 36 | *) 37 | -------------------------------------------------------------------------------- /resources/02code/28_another_expression_example.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Another Expression Example *) 3 | 4 | datatype exp = Constant of int 5 | | Negate of exp 6 | | Add of exp * exp 7 | | Multiply of exp * exp 8 | 9 | (* Note: example as explained in video assumes there is no library function 10 | for max of two ints. There is: Int.max *) 11 | 12 | fun max_constant e = 13 | let fun max_of_two (e1,e2) = 14 | let val m1 = max_constant e1 15 | val m2 = max_constant e2 16 | in 17 | if m1 > m2 then m1 else m2 18 | end 19 | in 20 | case e of 21 | Constant i => i 22 | | Negate e2 => max_constant e2 23 | | Add(e1,e2) => max_of_two(e1,e2) 24 | | Multiply(e1,e2) => max_of_two(e1,e2) 25 | end 26 | 27 | val test_exp = Add (Constant 19, Negate (Constant 4)) 28 | val nineteen = max_constant test_exp 29 | 30 | (* Note: Using Int.max, we don't need a local helper function. 31 | This second version is fewer lines, but there is a bit more 32 | code copying. *) 33 | 34 | fun max_constant2 e = 35 | case e of 36 | Constant i => i 37 | | Negate e2 => max_constant2 e2 38 | | Add(e1,e2) => Int.max(max_constant2 e1, max_constant2 e2) 39 | | Multiply(e1,e2) => Int.max(max_constant2 e1, max_constant2 e2) 40 | -------------------------------------------------------------------------------- /resources/02code/29_list_and_option_datatypes.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Lists and Options are Datatypes *) 3 | 4 | datatype my_int_list = Empty 5 | | Cons of int * my_int_list 6 | 7 | val one_two_three = Cons(1,Cons(2,Cons(3,Empty))) 8 | 9 | fun append_mylist (xs,ys) = 10 | case xs of 11 | Empty => ys 12 | | Cons(x,xs') => Cons(x, append_mylist(xs',ys)) 13 | 14 | fun inc_or_zero intoption = 15 | case intoption of 16 | NONE => 0 17 | | SOME i => i+1 18 | 19 | fun sum_list xs = 20 | case xs of 21 | [] => 0 22 | | x::xs' => x + sum_list xs' 23 | 24 | fun append (xs,ys) = 25 | case xs of 26 | [] => ys 27 | | x::xs' => x :: append(xs',ys) 28 | -------------------------------------------------------------------------------- /resources/02code/30_polymorphic_datatypes.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Polymorphic Datatypes *) 3 | 4 | (* type is int list -> int *) 5 | fun sum_list xs = 6 | case xs of 7 | [] => 0 8 | | x::xs' => x + sum_list xs' 9 | 10 | (* type is 'a list * 'a list -> 'a list *) 11 | fun append (xs,ys) = 12 | case xs of 13 | [] => ys 14 | | x::xs' => x :: append(xs',ys) 15 | 16 | (* this really is /exactly/ how options are defined 17 | careful: now shadowing the built in ones! 18 | *) 19 | 20 | datatype 'a option = NONE | SOME of 'a 21 | 22 | (* similarly, here are polymorphic lists but without special syntax *) 23 | 24 | datatype 'a mylist = Empty | Cons of 'a * 'a mylist 25 | 26 | (* a fancier example for binary trees where internal nodes have data of 27 | any one type and leaves have data of another possibly-different type *) 28 | 29 | datatype ('a,'b) tree = Node of 'a * ('a,'b) tree * ('a,'b) tree 30 | | Leaf of 'b 31 | 32 | (* type is (int,int) tree -> int *) 33 | fun sum_tree tr = 34 | case tr of 35 | Leaf i => i 36 | | Node(i,lft,rgt) => i + sum_tree lft + sum_tree rgt 37 | 38 | (* type is ('a,int) tree -> int *) 39 | fun sum_leaves tr = 40 | case tr of 41 | Leaf i => i 42 | | Node(i,lft,rgt) => sum_leaves lft + sum_leaves rgt 43 | 44 | (* type is ('a,'b) tree -> int *) 45 | fun num_leaves tr = 46 | case tr of 47 | Leaf i => 1 48 | | Node(i,lft,rgt) => num_leaves lft + num_leaves rgt 49 | -------------------------------------------------------------------------------- /resources/02code/31_eachof_pattern_matching.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Pattern-Matching for Each-Of Types: 3 | The Truth About Function Arguments *) 4 | 5 | fun sum_triple1 (triple : int * int * int) = 6 | case triple of 7 | (x,y,z) => z + y + x 8 | 9 | fun full_name1 (r : {first:string,middle:string,last:string}) = 10 | case r of 11 | {first=x,middle=y,last=z} => x ^ " " ^ y ^ " " ^z 12 | 13 | fun full_name2 (r : {first:string,middle:string,last:string}) = 14 | let val {first=x,middle=y,last=z} = r 15 | in 16 | x ^ " " ^ y ^ " " ^z 17 | end 18 | 19 | fun sum_triple2 triple = 20 | let val (x,y,z) = triple 21 | in 22 | x + y + z 23 | end 24 | 25 | fun full_name3 {first=x,middle=y,last=z} = 26 | x ^ " " ^ y ^ " " ^z 27 | 28 | fun sum_triple3 (x,y,z) = 29 | x + y + z 30 | 31 | fun rotate_left (x,y,z) = (y,z,x) 32 | 33 | fun rotate_right triple = rotate_left(rotate_left triple) 34 | -------------------------------------------------------------------------------- /resources/02code/32_type_inference.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: A Little Type Inference *) 3 | 4 | fun sum_triple1 (x, y, z) = 5 | x + y + z 6 | 7 | fun full_name1 {first=x, middle=y, last=z} = 8 | x ^ " " ^ y ^ " " ^ z 9 | 10 | (* these versions will not type-check without type annotations because 11 | the type-checker cannot figure out if there might be other fields *) 12 | fun sum_triple2 (triple : int*int*int) = 13 | #1 triple + #2 triple + #3 triple 14 | 15 | fun full_name2 (r : {first:string, middle:string, 16 | last:string}) = 17 | #first r ^ " " ^ #middle r ^ " " ^ #last r 18 | 19 | (* these functions are polymorphic: type of y can be anything *) 20 | 21 | fun partial_sum (x, y, z) = 22 | x + z 23 | 24 | fun partial_name {first=x, middle=y, last=z} = 25 | x ^ " " ^ z 26 | -------------------------------------------------------------------------------- /resources/02code/33_polymorphic_and_equality_types.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Polymorphic Types and Equality Types *) 3 | 4 | fun append (xs,ys) = 5 | case xs of 6 | [] => ys 7 | | x::xs' => x :: append(xs',ys) 8 | 9 | val ok1 = append(["hi","bye"],["programming","languages"]) 10 | 11 | val ok2 = append([1,2],[4,5]); 12 | 13 | (* 14 | val not_ok = append([1,2],["programming","languages"]) 15 | *) 16 | 17 | (* has type ''a * ''a -> string *) 18 | fun same_thing(x,y) = if x=y then "yes" else "no" 19 | 20 | (* has type int -> string *) 21 | fun is_three x = if x=3 then "yes" else "no" 22 | -------------------------------------------------------------------------------- /resources/02code/34_nested_patterns.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Nested Patterns *) 3 | 4 | exception ListLengthMismatch 5 | 6 | (* don't do this *) 7 | fun old_zip3 (l1,l2,l3) = 8 | if null l1 andalso null l2 andalso null l3 9 | then [] 10 | else if null l1 orelse null l2 orelse null l3 11 | then raise ListLengthMismatch 12 | else (hd l1, hd l2, hd l3) :: old_zip3(tl l1, tl l2, tl l3) 13 | 14 | (* don't do this *) 15 | fun shallow_zip3 (l1,l2,l3) = 16 | case l1 of 17 | [] => 18 | (case l2 of 19 | [] => (case l3 of 20 | [] => [] 21 | | _ => raise ListLengthMismatch) 22 | | _ => raise ListLengthMismatch) 23 | | hd1::tl1 => 24 | (case l2 of 25 | [] => raise ListLengthMismatch 26 | | hd2::tl2 => (case l3 of 27 | [] => raise ListLengthMismatch 28 | | hd3::tl3 => 29 | (hd1,hd2,hd3)::shallow_zip3(tl1,tl2,tl3))) 30 | 31 | (* do this *) 32 | fun zip3 list_triple = 33 | case list_triple of 34 | ([],[],[]) => [] 35 | | (hd1::tl1,hd2::tl2,hd3::tl3) => (hd1,hd2,hd3)::zip3(tl1,tl2,tl3) 36 | | _ => raise ListLengthMismatch 37 | 38 | (* and the inverse *) 39 | fun unzip3 lst = 40 | case lst of 41 | [] => ([],[],[]) 42 | | (a,b,c)::tl => let val (l1,l2,l3) = unzip3 tl 43 | in 44 | (a::l1,b::l2,c::l3) 45 | end 46 | -------------------------------------------------------------------------------- /resources/02code/35_more_nested_patterns.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: More Nested Patterns *) 3 | 4 | (* another elegant use of "deep" patterns *) 5 | fun nondecreasing xs = 6 | case xs of 7 | [] => true 8 | | x::[] => true 9 | | head::(neck::rest) => (head <= neck andalso nondecreasing (neck::rest)) 10 | 11 | (* nested pattern-matching often convenient even without recursion; 12 | also the wildcard pattern is good style 13 | match on a pair and one or more parts of it quite useful on homework 2 14 | *) 15 | datatype sgn = P | N | Z 16 | 17 | fun multsign (x1,x2) = 18 | let fun sign x = if x=0 then Z else if x>0 then P else N 19 | in 20 | case (sign x1,sign x2) of 21 | (Z,_) => Z 22 | | (_,Z) => Z 23 | | (P,P) => P 24 | | (N,N) => P 25 | | _ => N (* many say bad style; I am okay with it *) 26 | end 27 | 28 | (* simpler use of wildcard pattern for when you do not need the data *) 29 | 30 | fun len xs = 31 | case xs of 32 | [] => 0 33 | | _::xs' => 1 + len xs' 34 | -------------------------------------------------------------------------------- /resources/02code/37_function_patterns.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: *Optional*: Function Patterns *) 3 | 4 | datatype exp = Constant of int 5 | | Negate of exp 6 | | Add of exp * exp 7 | | Multiply of exp * exp 8 | 9 | fun old_eval e = 10 | case e of 11 | Constant i => i 12 | | Negate e2 => ~ (old_eval e2) 13 | | Add(e1,e2) => (old_eval e1) + (old_eval e2) 14 | | Multiply(e1,e2) => (old_eval e1) * (old_eval e2) 15 | 16 | fun eval (Constant i) = i 17 | | eval (Negate e2) = ~ (eval e2) 18 | | eval (Add(e1,e2)) = (eval e1) + (eval e2) 19 | | eval (Multiply(e1,e2)) = (eval e1) * (eval e2) 20 | 21 | fun append ([],ys) = ys 22 | | append (x::xs',ys) = x :: append(xs',ys) 23 | -------------------------------------------------------------------------------- /resources/02code/38_exceptions.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Exceptions *) 3 | 4 | fun hd xs = 5 | case xs of 6 | [] => raise List.Empty 7 | | x::_ => x 8 | 9 | exception MyUndesirableCondition 10 | 11 | exception MyOtherException of int * int 12 | 13 | fun mydiv (x,y) = 14 | if y=0 15 | then raise MyUndesirableCondition 16 | else x div y 17 | 18 | fun maxlist (xs,ex) = (* int list * exn -> int *) 19 | case xs of 20 | [] => raise ex 21 | | x::[] => x 22 | | x::xs' => Int.max(x,maxlist(xs',ex)) 23 | 24 | val w = maxlist ([3,4,5],MyUndesirableCondition) (* 5 *) 25 | 26 | val x = maxlist ([3,4,5],MyUndesirableCondition) (* 5 *) 27 | handle MyUndesirableCondition => 42 28 | 29 | (*val y = maxlist ([],MyUndesirableCondition)*) 30 | 31 | val z = maxlist ([],MyUndesirableCondition) (* 42 *) 32 | handle MyUndesirableCondition => 42 33 | -------------------------------------------------------------------------------- /resources/02code/39_tail_recursion.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Tail Recursion *) 3 | 4 | fun fact1 n = if n=0 then 1 else n * fact1(n-1) 5 | 6 | fun fact2 n = 7 | let fun aux(n,acc) = if n=0 then acc else aux(n-1,acc*n) 8 | in 9 | aux(n,1) 10 | end 11 | -------------------------------------------------------------------------------- /resources/02code/40_accumulators.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 2: Accumulators *) 3 | 4 | fun sum1 xs = 5 | case xs of 6 | [] => 0 7 | | i::xs' => i + sum1 xs' 8 | 9 | fun sum2 xs = 10 | let fun f (xs,acc) = 11 | case xs of 12 | [] => acc 13 | | i::xs' => f(xs',i+acc) 14 | in 15 | f(xs,0) 16 | end 17 | 18 | fun rev1 xs = 19 | case xs of 20 | [] => [] 21 | | x::xs' => (rev1 xs') @ [x] 22 | 23 | fun rev2 xs = 24 | let fun aux(xs,acc) = 25 | case xs of 26 | [] => acc 27 | | x::xs' => aux(xs', x::acc) 28 | in 29 | aux(xs,[]) 30 | end 31 | -------------------------------------------------------------------------------- /resources/02summary.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/resources/02summary.pdf -------------------------------------------------------------------------------- /resources/03code/42_functions_intro.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Introduction to First-Class Functions *) 3 | 4 | fun double x = 2*x 5 | fun incr x = x+1 6 | val a_tuple = (double, incr, double(incr 7)) 7 | val eighteen = (#1 a_tuple) 9 8 | -------------------------------------------------------------------------------- /resources/03code/43_functions_as_arguments.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Functions As Arguments *) 3 | 4 | (* it should *pain* us to write the next three functions separately, 5 | but we do not have to *) 6 | fun increment_n_times_lame (n,x) = (* silly example, this is addition (n+x) *) 7 | if n=0 8 | then x 9 | else 1 + increment_n_times_lame(n-1,x) 10 | 11 | fun double_n_times_lame (n,x) = 12 | if n=0 13 | then x 14 | else 2 * double_n_times_lame(n-1,x) 15 | 16 | fun nth_tail_lame (n,xs) = 17 | if n=0 18 | then xs 19 | else tl (nth_tail_lame(n-1,xs)) 20 | 21 | (* this is much better: as always, abstract the common pieces into a function 22 | n_times(f,n,x) returns f(f(...(f(x)))) where there are n calls to f 23 | note: if we gave x type int, then we could not use this for lists 24 | *) 25 | fun n_times (f,n,x) = 26 | if n=0 27 | then x 28 | else f (n_times(f,n-1,x)) 29 | 30 | fun increment x = x+1 31 | 32 | fun double x = x+x 33 | 34 | val x1 = n_times(double,4,7) 35 | val x2 = n_times(increment,4,7) 36 | val x3 = n_times(tl,2,[4,8,12,16]) 37 | 38 | (* and we can define functions that use n_times *) 39 | fun addition (n,x) = n_times(increment,n,x) (* assumes n >=0 *) 40 | fun double_n_times (n,x) = n_times(double,n,x) 41 | fun nth_tail (n,x) = n_times(tl,n,x) 42 | 43 | (* we can then use n_times for more things we did not plan on originally *) 44 | 45 | fun triple x = 3*x 46 | 47 | fun triple_n_times (n,x) = n_times(triple,n,x) 48 | 49 | 50 | -------------------------------------------------------------------------------- /resources/03code/44_types_and_functions.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Polymorphic Types and Functions As Arguments *) 3 | 4 | (* our n_times function is polymorphic, which lets us use it for numbers, lists, 5 | or anything else provided f and x "agree" 6 | - return and argument type of f must be the same here because and only 7 | because result is passed back to f 8 | *) 9 | fun n_times (f,n,x) = 10 | if n=0 11 | then x 12 | else f (n_times(f,n-1,x)) 13 | 14 | fun increment x = x+1 15 | fun double x = x+x 16 | val x1 = n_times(double,4,7) (* instantiates 'a with int *) 17 | val x2 = n_times(increment,4,7) (* instantiates 'a with int *) 18 | val x3 = n_times(tl,2,[4,8,12,16]) (* instantiates 'a with int list *) 19 | 20 | (* higher-order functions are often polymorphic based on "whatever 21 | type of function is passed" but not always: *) 22 | fun times_until_zero (f,x) = 23 | (* note: a better implementation would be tail-recursive *) 24 | if x=0 then 0 else 1 + times_until_zero(f, f x) 25 | 26 | (* conversely, we have seen polymorphic functions that are not higher-order *) 27 | fun len xs = 28 | case xs of 29 | [] => 0 30 | | x::xs' => 1 + len xs' 31 | 32 | -------------------------------------------------------------------------------- /resources/03code/45_anonymous_functions.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Anonymous Functions *) 3 | 4 | fun n_times (f,n,x) = 5 | if n=0 6 | then x 7 | else f (n_times(f,n-1,x)) 8 | 9 | fun triple x = 3*x 10 | 11 | fun triple_n_times1 (n,x) = n_times(triple,n,x) 12 | 13 | fun triple_n_times2 (n,x) = 14 | let fun triple x = 3*x in n_times(triple,n,x) end 15 | 16 | (* actually since used only once, we could define it 17 | right where we need it *) 18 | fun triple_n_times3 (n,x) = 19 | n_times((let fun triple y = 3*y in triple end), n, x) 20 | 21 | (* This does not work: a function /binding/ is not an /expression/ *) 22 | (* fun triple_n_times3 (n,x) = n_times((fun triple y = 3*y), n, x) *) 23 | 24 | (* This /anonymous function/ expression works and is the best style: *) 25 | (* Notice the function has no name *) 26 | 27 | fun triple_n_times4 (n,x) = n_times((fn y => 3*y), n, x) 28 | 29 | (* because triple_n_times4 does not call itself, we could use a val-binding 30 | to define it, but the fun binding above is better style *) 31 | val triple_n_times5 = fn (n,x) => n_times((fn y => 3*y), n, x) 32 | -------------------------------------------------------------------------------- /resources/03code/46_unnecessary_function_wrapping.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Unnecessary Function Wrapping *) 3 | 4 | fun n_times (f,n,x) = 5 | if n=0 6 | then x 7 | else f (n_times(f,n-1,x)) 8 | 9 | (* bad style: the if e then true else false of functions *) 10 | fun nth_tail (n,xs) = n_times((fn y => tl y), n, xs) 11 | 12 | (* good style: *) 13 | fun nth_tail (n,x) = n_times(tl, n, x) 14 | 15 | (* bad style *) 16 | fun rev xs = List.rev xs 17 | 18 | val rev = fn xs => List.rev xs 19 | 20 | (* good style *) 21 | val rev = List.rev 22 | -------------------------------------------------------------------------------- /resources/03code/47_map_and_filter.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Map and Filter *) 3 | 4 | (* here is a very, very useful and common example *) 5 | fun map (f,xs) = 6 | case xs of 7 | [] => [] 8 | | x::xs' => (f x)::(map(f,xs')) 9 | 10 | val x1 = map ((fn x => x+1), [4,8,12,16]) 11 | 12 | val x2 = map (hd, [[1,2],[3,4],[5,6,7]]) 13 | 14 | (* another very, very useful and common example *) 15 | fun filter (f,xs) = 16 | case xs of 17 | [] => [] 18 | | x::xs' => if f x 19 | then x::(filter (f,xs')) 20 | else filter (f,xs') 21 | 22 | fun is_even v = 23 | (v mod 2 = 0) 24 | 25 | fun all_even xs = 26 | filter(is_even,xs) 27 | 28 | fun all_even_snd xs = 29 | filter((fn (_,v) => is_even v), xs) 30 | 31 | -------------------------------------------------------------------------------- /resources/03code/48_generalizing_prior_topics.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Generalizing Prior Topics *) 3 | 4 | (* Returning a function *) 5 | fun double_or_triple f = 6 | if f 7 7 | then fn x => 2*x 8 | else fn x => 3*x 9 | 10 | val double = double_or_triple (fn x => x-3 = 4) 11 | val nine = (double_or_triple (fn x => x = 42)) 3 12 | 13 | (* Higher-order functions over our own datatype bindings *) 14 | datatype exp = Constant of int 15 | | Negate of exp 16 | | Add of exp * exp 17 | | Multiply of exp * exp 18 | 19 | fun true_of_all_constants(f,e) = 20 | case e of 21 | Constant i => f i 22 | | Negate e1 => true_of_all_constants(f,e1) 23 | | Add(e1,e2) => true_of_all_constants(f,e1) 24 | andalso true_of_all_constants(f,e2) 25 | | Multiply(e1,e2) => true_of_all_constants(f,e1) 26 | andalso true_of_all_constants(f,e2) 27 | 28 | fun all_even e = true_of_all_constants((fn x => x mod 2 = 0),e) 29 | -------------------------------------------------------------------------------- /resources/03code/49_lexical_scope.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Lexical Scope *) 3 | 4 | (* 1 *) val x = 1 5 | 6 | (* 2 *) fun f y = x + y 7 | 8 | (* 3 *) val x = 2 9 | 10 | (* 4 *) val y = 3 11 | 12 | (* 5 *) val z = f (x + y) 13 | 14 | -------------------------------------------------------------------------------- /resources/03code/50_lexical_scope_and_functions.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Lexical Scope and Higher-Order Functions *) 3 | 4 | (* first example *) 5 | val x = 1 6 | fun f y = 7 | let 8 | val x = y+1 9 | in 10 | fn z => x + y + z 11 | end 12 | val x = 3 13 | val g = f 4 14 | val y = 5 15 | val z = g 6 16 | 17 | (* second example *) 18 | fun f g = 19 | let 20 | val x = 3 21 | in 22 | g 2 23 | end 24 | val x = 4 25 | fun h y = x + y 26 | val z = f h 27 | -------------------------------------------------------------------------------- /resources/03code/51_why_lexical_scope.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Why Lexical Scope *) 3 | 4 | (* f1 and f2 are always the same, no matter where the result is used *) 5 | 6 | fun f1 y = 7 | let 8 | val x = y + 1 9 | in 10 | fn z => x + y + z 11 | end 12 | 13 | fun f2 y = 14 | let 15 | val q = y + 1 16 | in 17 | fn z => q + y + z 18 | end 19 | 20 | val x = 17 (* irrelevant *) 21 | val a1 = (f1 7) 4 22 | val a2 = (f2 7) 4 23 | 24 | (* f3 and f4 are always the same, no matter what argument is passed in *) 25 | 26 | fun f3 g = 27 | let 28 | val x = 3 (* irrelevant *) 29 | in 30 | g 2 31 | end 32 | 33 | fun f4 g = 34 | g 2 35 | 36 | val x = 17 37 | val a3 = f3 (fn y => x + y) 38 | val a4 = f3 (fn y => 17 + y) 39 | 40 | (* under dynamic scope, the call "g 6" below would try to add a string 41 | (from looking up x) and would have an unbound variable (looking up y), 42 | even though f1 type-checked with type int -> (int -> int) *) 43 | 44 | val x = "hi" 45 | val g = f1 7 46 | val z = g 4 47 | 48 | (* Being able to pass closures that have free variables (private data) 49 | makes higher-order functions /much/ more useful *) 50 | fun filter (f,xs) = 51 | case xs of 52 | [] => [] 53 | | x::xs' => if f x then x::(filter(f,xs')) else filter(f,xs') 54 | 55 | fun greaterThanX x = fn y => y > x 56 | 57 | fun noNegatives xs = filter(greaterThanX ~1, xs) 58 | 59 | fun allGreater (xs,n) = filter (fn x => x > n, xs) 60 | 61 | -------------------------------------------------------------------------------- /resources/03code/52_closures_and_recomputation.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Closures and Recomputation *) 3 | 4 | fun filter (f,xs) = 5 | case xs of 6 | [] => [] 7 | | x::xs' => if f x then x::(filter(f,xs')) else filter(f,xs') 8 | 9 | fun allShorterThan1 (xs,s) = 10 | filter (fn x => String.size x < (print "!"; String.size s), xs) 11 | 12 | fun allShorterThan2 (xs,s) = 13 | let 14 | val i = (print "!"; String.size s) 15 | in 16 | filter(fn x => String.size x < i, xs) 17 | end 18 | 19 | val _ = print "\nwithAllShorterThan1: " 20 | 21 | val x1 = allShorterThan1(["1","333","22","4444"],"xxx") 22 | 23 | val _ = print "\nwithAllShorterThan2: " 24 | 25 | val x2 = allShorterThan2(["1","333","22","4444"],"xxx") 26 | 27 | val _ = print "\n" 28 | -------------------------------------------------------------------------------- /resources/03code/53_fold_and_more_closures.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Fold and More Closures *) 3 | 4 | (* Another hall-of-fame higher-order function *) 5 | 6 | (* note this is "fold left" if order matters 7 | can also do "fold right" *) 8 | fun fold (f,acc,xs) = 9 | case xs of 10 | [] => acc 11 | | x::xs' => fold (f,f(acc,x),xs') 12 | 13 | (* examples not using private data *) 14 | 15 | fun f1 xs = fold ((fn (x,y) => x+y), 0, xs) 16 | 17 | fun f2 xs = fold ((fn (x,y) => x andalso y >= 0), true, xs) 18 | 19 | (* examples using private data *) 20 | 21 | fun f3 (xs,lo,hi) = 22 | fold ((fn (x,y) => 23 | x + (if y >= lo andalso y <= hi then 1 else 0)), 24 | 0, xs) 25 | 26 | fun f4 (xs,s) = 27 | let 28 | val i = String.size s 29 | in 30 | fold((fn (x,y) => x andalso String.size y < i), true, xs) 31 | end 32 | 33 | fun f5 (g,xs) = fold((fn(x,y) => x andalso g y), true, xs) 34 | 35 | fun f4again (xs,s) = 36 | let 37 | val i = String.size s 38 | in 39 | f5(fn y => String.size y < i, xs) 40 | end 41 | 42 | -------------------------------------------------------------------------------- /resources/03code/54_combining_functions.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Another Closure Idiom: Combining Functions *) 3 | 4 | fun compose (f,g) = fn x => f (g x) 5 | 6 | fun sqrt_of_abs i = Math.sqrt(Real.fromInt (abs i)) 7 | 8 | fun sqrt_of_abs i = (Math.sqrt o Real.fromInt o abs) i 9 | 10 | val sqrt_of_abs = Math.sqrt o Real.fromInt o abs 11 | 12 | (* tells the parser !> is a function that appears between its two arguments *) 13 | infix !> 14 | 15 | (* operator more commonly written |>, but that confuses the current version 16 | of SML Mode for Emacs, leading to bad editing and formatting *) 17 | 18 | (* definition of the pipeline operator *) 19 | fun x !> f = f x 20 | 21 | fun sqrt_of_abs i = i !> abs !> Real.fromInt !> Math.sqrt 22 | 23 | fun backup1 (f,g) = fn x => case f x of NONE => g x | SOME y => y 24 | 25 | fun backup2 (f,g) = fn x => f x handle _ => g x 26 | -------------------------------------------------------------------------------- /resources/03code/55_currying.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Another Closure Idiom: Currying *) 3 | 4 | (* old way to get the effect of multiple arguments *) 5 | fun sorted3_tupled (x,y,z) = z >= y andalso y >= x 6 | 7 | val t1 = sorted3_tupled (7,9,11) 8 | 9 | (* new way: currying *) 10 | val sorted3 = fn x => fn y => fn z => z >= y andalso y >= x 11 | 12 | (* alternately: fun sorted3 x = fn y => fn z => z >= y andalso y >= x *) 13 | 14 | val t2 = ((sorted3 7) 9) 11 15 | 16 | (* syntactic sugar for calling curried functions: optional parentheses *) 17 | val t3 = sorted3 7 9 11 18 | 19 | (* syntactic sugar for defining curried functions: space between arguments *) 20 | fun sorted3_nicer x y z = z >= y andalso y >= x 21 | 22 | (* more calls that work: *) 23 | val t4 = sorted3_nicer 7 9 11 24 | val t5 = ((sorted3_nicer 7) 9) 11 25 | 26 | (* calls that do not work: cannot mix tupling and currying *) 27 | (*val wrong1 = ((sorted3_tupled 7) 9) 11*) 28 | (*val wrong2 = sorted3_tupled 7 9 11*) 29 | (*val wrong3 = sorted3 (7,9,11)*) 30 | (*val wrong4 = sorted3_nicer (7,9,11)*) 31 | 32 | (* a more useful example *) 33 | fun fold f acc xs = (* means fun fold f = fn acc => fn xs => *) 34 | case xs of 35 | [] => acc 36 | | x::xs' => fold f (f(acc,x)) xs' 37 | (* Note: foldl in the ML standard library is very similar, but 38 | the two arguments for the function f are in the opposite order. 39 | The order is, naturally, a matter of taste. 40 | *) 41 | 42 | (* a call to curried fold: will improve this call next *) 43 | fun sum xs = fold (fn (x,y) => x+y) 0 xs 44 | 45 | -------------------------------------------------------------------------------- /resources/03code/56_partial_application.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Partial Application *) 3 | 4 | fun sorted3 x y z = z >= y andalso y >= x 5 | 6 | fun fold f acc xs = (* means fun fold f = fn acc => fn xs => *) 7 | case xs of 8 | [] => acc 9 | | x::xs' => fold f (f(acc,x)) xs' 10 | 11 | (* If a curried function is applied to "too few" arguments, that just returns 12 | a closure, which is often useful -- a powerful idiom (no new semantics) *) 13 | 14 | val is_nonnegative = sorted3 0 0 15 | 16 | val sum = fold (fn (x,y) => x+y) 0 17 | 18 | (* In fact, not doing this is often a harder-to-notice version of 19 | unnecessary function wrapping, as in these inferior versions *) 20 | 21 | fun is_nonnegative_inferior x = sorted3 0 0 x 22 | 23 | fun sum_inferior xs = fold (fn (x,y) => x+y) 0 xs 24 | 25 | (* another example *) 26 | 27 | fun range i j = if i > j then [] else i :: range (i+1) j 28 | 29 | val countup = range 1 30 | 31 | fun countup_inferior x = range 1 x 32 | 33 | (* common style is to curry higher-order functions with function arguments 34 | first to enable convenient partial application *) 35 | 36 | fun exists predicate xs = 37 | case xs of 38 | [] => false 39 | | x::xs' => predicate x orelse exists predicate xs' 40 | 41 | val no = exists (fn x => x=7) [4,11,23] 42 | 43 | val hasZero = exists (fn x => x=0) 44 | 45 | val incrementAll = List.map (fn x => x + 1) 46 | 47 | (* library functions foldl, List.filter, etc. also generally curried: *) 48 | 49 | val removeZeros = List.filter (fn x => x <> 0) 50 | 51 | (* but if you get a strange message about "value restriction", just put back 52 | in the actually-necessary wrapping or an explicit non-polymorphic type *) 53 | 54 | (* does not work for reasons we will not explain here (more later) *) 55 | (* (only an issue will polymorphic functions) *) 56 | 57 | (* val pairWithOne = List.map (fn x => (x,1)) *) 58 | 59 | (* workarounds: *) 60 | fun pairWithOne xs = List.map (fn x => (x,1)) xs 61 | 62 | val pairWithOne : string list -> (string * int) list = List.map (fn x => (x,1)) 63 | 64 | (* this different function works fine because result is not polymorphic *) 65 | val incrementAndPairWithOne = List.map (fn x => (x+1,1)) 66 | -------------------------------------------------------------------------------- /resources/03code/57_currying_wrapup.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Currying Wrapup *) 3 | 4 | (* generic functions to switch how/whether currying is used *) 5 | (* in each case, the type tells you a lot *) 6 | 7 | fun curry f x y = f (x,y) 8 | 9 | fun uncurry f (x,y) = f x y 10 | 11 | fun other_curry1 f = fn x => fn y => f y x 12 | 13 | fun other_curry2 f x y = f y x 14 | 15 | (* example *) 16 | 17 | (* tupled but we wish it were curried *) 18 | fun range (i,j) = if i > j then [] else i :: range(i+1, j) 19 | 20 | (* no problem *) 21 | val countup = curry range 1 22 | 23 | val xs = countup 7 24 | -------------------------------------------------------------------------------- /resources/03code/58_mutable_references.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Mutable References *) 3 | 4 | val x = ref 42 5 | 6 | val y = ref 42 7 | 8 | val z = x 9 | 10 | val _ = x := 43 11 | 12 | val w = (!y) + (!z) (* 85 *) 13 | 14 | (* x + 1 does not type-check *) 15 | -------------------------------------------------------------------------------- /resources/03code/59_callbacks.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Callbacks *) 3 | 4 | (* these two bindings would be internal (private) to the library *) 5 | val cbs : (int -> unit) list ref = ref [] 6 | fun onEvent i = 7 | let fun loop fs = 8 | case fs of 9 | [] => () 10 | | f::fs' => (f i; loop fs') 11 | in loop (!cbs) end 12 | 13 | (* clients call only this function (public interface to the library) *) 14 | fun onKeyEvent f = cbs := f::(!cbs) 15 | 16 | (* some clients where closures are essential 17 | notice different environments use bindings of different types 18 | *) 19 | val timesPressed = ref 0 20 | val _ = onKeyEvent (fn _ => timesPressed := (!timesPressed) + 1) 21 | 22 | fun printIfPressed i = 23 | onKeyEvent (fn j => if i=j 24 | then print ("you pressed " ^ Int.toString i ^ "\n") 25 | else ()) 26 | 27 | val _ = printIfPressed 4 28 | val _ = printIfPressed 11 29 | val _ = printIfPressed 23 30 | val _ = printIfPressed 4 31 | 32 | -------------------------------------------------------------------------------- /resources/03code/61_adts_with_closures.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Optional: Abstract Data Types with Closures *) 3 | 4 | (* a set of ints with three operations *) 5 | (* this interface is immutable -- insert returns a new set -- but we could 6 | also have implemented a mutable version using ML's references *) 7 | (* Note: a 1-constructor datatype is an SML trick for recursive types *) 8 | datatype set = S of { insert : int -> set, 9 | member : int -> bool, 10 | size : unit -> int } 11 | 12 | (* implementation of sets: this is the fancy stuff, but clients using 13 | this abstraction do not need to understand it *) 14 | val empty_set = 15 | let 16 | fun make_set xs = (* xs is a "private field" in result *) 17 | let (* contains a "private method" in result *) 18 | fun contains i = List.exists (fn j => i=j) xs 19 | in 20 | S { insert = fn i => if contains i 21 | then make_set xs 22 | else make_set (i::xs), 23 | member = contains, 24 | size = fn () => length xs 25 | } 26 | end 27 | in 28 | make_set [] 29 | end 30 | 31 | (* example client *) 32 | fun use_sets () = 33 | let val S s1 = empty_set 34 | val S s2 = (#insert s1) 34 35 | val S s3 = (#insert s2) 34 36 | val S s4 = #insert s3 19 37 | in 38 | if (#member s4) 42 39 | then 99 40 | else if (#member s4) 19 41 | then 17 + (#size s3) () 42 | else 0 43 | end 44 | -------------------------------------------------------------------------------- /resources/03code/62_without_closures.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 3: Optional: Closure Idioms Without Closures *) 3 | 4 | (* Note this file is sort of misnamed. It /does/ use closures. It is code 5 | that we will compare to code in Java or C that does not use closures. *) 6 | 7 | (* re-implementation of a list library with map, filter, length *) 8 | 9 | datatype 'a mylist = Cons of 'a * ('a mylist) | Empty 10 | 11 | fun map f xs = 12 | case xs of 13 | Empty => Empty 14 | | Cons(x,xs) => Cons(f x, map f xs) 15 | 16 | fun filter f xs = 17 | case xs of 18 | Empty => Empty 19 | | Cons(x,xs) => if f x then Cons(x,filter f xs) else filter f xs 20 | 21 | fun length xs = 22 | case xs of 23 | Empty => 0 24 | | Cons(_,xs) => 1 + length xs 25 | 26 | (* One fine way to double all numbers in a list *) 27 | 28 | val doubleAll = map (fn x => x * 2) 29 | 30 | (* One way to count Ns in a list *) 31 | 32 | fun countNs (xs, n : int) = length (filter (fn x => x=n) xs) 33 | 34 | 35 | -------------------------------------------------------------------------------- /resources/03code/63_java_without_closures.java: -------------------------------------------------------------------------------- 1 | // Programming Languages, Dan Grossman 2 | // Section 3: Optional: Closure Idioms Without Closures in Java 3 | 4 | // Note: This code compiles but has not been carefully tested. 5 | // Bug reports welcome. 6 | 7 | 8 | interface Func { 9 | B m(A x); 10 | } 11 | interface Pred { 12 | boolean m(A x); 13 | } 14 | 15 | class List { 16 | T head; 17 | List tail; 18 | List(T x, List xs) { 19 | head = x; 20 | tail = xs; 21 | } 22 | 23 | // * the advantage of a static method is it allows xs to be null 24 | // -- a more OO way would be a subclass for empty lists 25 | // * a more efficient way in Java would be a messy while loop 26 | // where you keep a pointer to the previous element and mutate it 27 | // -- (try it if you do not believe it is messy) 28 | static List map(Func f, List xs) { 29 | if(xs==null) 30 | return null; 31 | return new List(f.m(xs.head), map(f,xs.tail)); 32 | } 33 | 34 | static List filter(Pred f, List xs) { 35 | if(xs==null) 36 | return null; 37 | if(f.m(xs.head)) 38 | return new List(xs.head, filter(f,xs.tail)); 39 | return filter(f,xs.tail); 40 | } 41 | 42 | // * again recursion would be more elegant but less efficient 43 | // * again an instance method would be more common, but then 44 | // all clients have to special-case null 45 | static int length(List xs) { 46 | int ans = 0; 47 | while(xs != null) { 48 | ++ans; 49 | xs = xs.tail; 50 | } 51 | return ans; 52 | } 53 | } 54 | 55 | class ExampleClients { 56 | static List doubleAll(List xs) { 57 | return List.map((new Func() { 58 | public Integer m(Integer x) { return x * 2; } 59 | }), 60 | xs); 61 | } 62 | static int countNs(List xs, final int n) { 63 | return List.length(List.filter( 64 | (new Pred() { 65 | public boolean m(Integer x) { return x==n; } 66 | }), 67 | xs)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /resources/03code/64_c_without_closures.c: -------------------------------------------------------------------------------- 1 | // Programming Languages, Dan Grossman 2 | // Section 3: Optional: Closure Idioms Without Closures in C 3 | 4 | // Note: This code compiles but has not been carefully tested. 5 | // Bug reports welcome. 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | // The key point is that function pointers are only code pointers 12 | 13 | // Rather than create structs for closures, which would work fine, 14 | // we follow common C practice of having higher-order functions 15 | // take explicit environment fields as another argument 16 | // -- if they don't, then they are much less useful 17 | 18 | // void* requires lots of unchecked conversions between types, 19 | // but C has no notion of type variables 20 | 21 | typedef struct List list_t; 22 | struct List { 23 | void * head; 24 | list_t * tail; 25 | }; 26 | 27 | list_t * makelist (void * x, list_t * xs) { 28 | list_t * ans = (list_t *)malloc(sizeof(list_t)); 29 | ans->head = x; 30 | ans->tail = xs; 31 | return ans; 32 | } 33 | 34 | // as in the Java version, we show simple recursive solutions because 35 | // the loop-based ones require mutation and previous pointers. 36 | // But the more important point is the explicit env field passed to the 37 | // function pointer 38 | list_t * map(void* (*f)(void*,void*), void* env, list_t * xs) { 39 | if(xs==NULL) 40 | return NULL; 41 | return makelist(f(env,xs->head), map(f,env,xs->tail)); 42 | } 43 | 44 | list_t * filter(bool (*f)(void*,void*), void* env, list_t * xs) { 45 | if(xs==NULL) 46 | return NULL; 47 | if(f(env,xs->head)) 48 | return makelist(xs->head, filter(f,env,xs->tail)); 49 | return filter(f,env,xs->tail); 50 | } 51 | 52 | int length(list_t* xs) { 53 | int ans = 0; 54 | while(xs != NULL) { 55 | ++ans; 56 | xs = xs->tail; 57 | } 58 | return ans; 59 | } 60 | 61 | // clients of our list implementation: 62 | // [the clients that cast from void* to intptr_t are technically not legal C, 63 | // as explained in detail below if curious] 64 | 65 | // awful type casts to match what map expects 66 | void* doubleInt(void* ignore, void* i) { 67 | return (void*)(((intptr_t)i)*2); 68 | } 69 | 70 | // assumes list holds intptr_t fields 71 | list_t * doubleAll(list_t * xs) { 72 | return map(doubleInt, NULL, xs); 73 | } 74 | 75 | // awful type casts to match what filter expects 76 | bool isN(void* n, void* i) { 77 | return ((intptr_t)n)==((intptr_t)i); 78 | } 79 | 80 | // assumes list hold intptr_t fields 81 | int countNs(list_t * xs, intptr_t n) { 82 | return length(filter(isN, (void*)n, xs)); 83 | } 84 | 85 | /* 86 | The promised explanation: Some of the client code above tries to use 87 | a number for the environment by using a number (intptr_t) where a 88 | pointer (void *) is expected. This is technically not allowed: any 89 | pointer (including void*) can be cast to intptr_t (always) and the 90 | result can be cast back to the pointer type, but that is different 91 | than starting with an intptr_t and casting it to void* and then back 92 | to intptr_t. 93 | 94 | It appears there is no legal, portable way to create a number that 95 | can be cast to void* and back. People do this sort of thing often, 96 | but the resulting code is not strictly portable. So what should we 97 | do for our closures example besides ignore this and write 98 | non-portable code using int or intptr_t? 99 | 100 | Option 1 is to use an int* for the environment, passing a pointer to the 101 | value we need. That is the most common approach and what we need to do for 102 | environments with more than one value in them anyway. For the examples above, 103 | it would work to pass the address of a stack-allocated int, but that works 104 | only because the higher-order functions we are calling will not store those 105 | pointers in places where they might be used later after the stack variable 106 | is deallocated. So it works fine for examples like map and filter, but would 107 | not work for callback idioms. For those, the pointer should refer to memory 108 | returned by malloc or a similar library function. 109 | 110 | Option 2 is to change our approach to have the higher-order functions use 111 | intptr_t for the type of the environment instead of void*. This works in 112 | general since other code can portably cast from any pointer type to 113 | intptr_t. It is a less standard approach -- one commonly sees void* used 114 | as we have in the code above. 115 | */ 116 | -------------------------------------------------------------------------------- /resources/03summary.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/resources/03summary.pdf -------------------------------------------------------------------------------- /resources/04code/66_what_is_type_inference.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: What is Type Inference *) 3 | 4 | 5 | fun f x = (* infer val f : int -> int *) 6 | if x > 3 7 | then 42 8 | else x * 2 9 | (* 10 | fun g x = (* report type error *) 11 | if x > 3 12 | then true 13 | else x * 2 14 | *) 15 | -------------------------------------------------------------------------------- /resources/04code/67_ml_type_inference.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: ML Type Inference *) 3 | 4 | val x = 42 (* val x : int *) 5 | 6 | fun f (y, z, w) = 7 | if y (* y must be bool *) 8 | then z + x (* z must be int *) 9 | else 0 (* both branches have same type *) 10 | (* f must return an int 11 | f must take a bool * int * ANYTHING 12 | so val f : bool * int * 'a -> int 13 | *) 14 | -------------------------------------------------------------------------------- /resources/04code/68_type_inference_examples.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: Type Inference Examples *) 3 | 4 | (* 5 | f : T1 -> T2 [must be a function; all functions take one argument] 6 | x : T1 [must have type of f's argument] 7 | 8 | y : T3 9 | z : T4 10 | 11 | T1 = T3 * T4 [else pattern-match in val-binding doesn't type-check] 12 | T3 = int [because (abs y) where abs : int -> int] 13 | T4 = int [because add z to an int] 14 | So T1 = int * int 15 | So (abs y) + z : int, so let-expression : int, so body : int, so T2=int 16 | So f : int * int -> int 17 | *) 18 | fun f x = 19 | let val (y,z) = x in 20 | (abs y) + z 21 | end 22 | 23 | (* 24 | sum : T1 -> T2 [must be a function; all functions take one argument] 25 | xs : T1 [must have type of f's argument] 26 | 27 | x : T3 [pattern match against T3 list] 28 | xs' : T3 list [pattern match against T3 list] 29 | 30 | T1 = T3 list [else pattern-match on xs doesn't type-check] 31 | 0 : int, so case-expresssion : int, so body : int, so T2=int 32 | T3 = int [because x : T3 and is argument to addition] 33 | T2 = int [because result of recursive call is argument to addition] 34 | sum xs' type-checks because xs' has type T3 list and T1 = T3 list 35 | case-expression type-checks because both branches have type int 36 | 37 | from T1 = T3 list and T3 = int, we know T1 = int list 38 | from that and T2 = int, we know f : int list -> int 39 | *) 40 | fun sum xs = 41 | case xs of 42 | [] => 0 43 | | x::xs' => x + (sum xs') 44 | 45 | (* 46 | type inference proceeds exactly like for sum for most of it: 47 | broken_sum : T1 -> T2 [must be a function; all functions take one argument] 48 | xs : T1 [must have type of f's argument] 49 | 50 | x : T3 [pattern match against T3 list] 51 | xs' : T3 list [pattern match against T3 list] 52 | 53 | T1 = T3 list [else pattern-match on xs doesn't type-check] 54 | 0 : int, so case-expresssion : int, so body : int, so T2=int 55 | T3 = int [because x : T3 and is argument to addition] 56 | T2 = int [because result of recursive call is argument to addition] 57 | 58 | but now to type-check (broken_sum x) we need T3 = T1 and T1 = T3 list, 59 | so we need T3 = T3 list, which is impossible for any T3. 60 | 61 | Note: The actual type-checker might gather facts in a different order and 62 | therefore report a different error, but it will report an error. 63 | *) 64 | fun broken_sum xs = 65 | case xs of 66 | [] => 0 67 | | x::xs' => x + (broken_sum x) 68 | 69 | -------------------------------------------------------------------------------- /resources/04code/69_polymorphic_examples.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: Polymorphic Examples *) 3 | 4 | (* 5 | First several steps are just like with sum from previous segment: 6 | 7 | length : T1 -> T2 [must be a function; all functions take one argument] 8 | xs : T1 [must have type of f's argument] 9 | 10 | x : T3 [pattern match against T3 list] 11 | xs' : T3 list [pattern match against T3 list] 12 | 13 | T1 = T3 list [else pattern-match on xs doesn't type-check] 14 | 15 | 0 : int, so case-expresssion : int, so body : int, so T2=int 16 | 17 | recursive call type-checks because xs' has type T3 list, which = T1 18 | and T2=int, so fine argument to addition 19 | 20 | so with all our constraints, length : T3 list -> int 21 | so 'a list -> int 22 | *) 23 | fun length xs = 24 | case xs of 25 | [] => 0 26 | | x::xs' => 1 + (length xs') 27 | 28 | 29 | (* 30 | f : T1 * T2 * T3 -> T4 31 | x : T1 32 | y : T2 33 | z : T3 34 | 35 | both conditional branches must have type T4 (the type of the function body), 36 | so T1 * T2 * T3 = T4 and T2 * T1 * T3 = T4, which means T1 = T2 37 | 38 | putting it all together, f : T1 * T1 * T3 -> T1 * T1 * T3 39 | now replace unconstrained types /consistently/ with type variables: 40 | 'a * 'a * 'b -> 'a * 'a * 'b 41 | *) 42 | fun f (x,y,z) = 43 | if true 44 | then (x,y,z) 45 | else (y,x,z) 46 | 47 | (* 48 | compose : T1 * T2 -> T3 49 | f : T1 50 | g : T2 51 | x : T4 52 | 53 | from body of compose being a function, T3 = T4->T5 for some T4 and T5 54 | from g being passed x, T2 = T4->T6 for some T6 55 | from f being passed result of g, T1 = T6->T7 for some T7 56 | from f being body of anonymous function, T7=T5 57 | 58 | putting it all together: 59 | T1=T6->T5, T2=T4->T6, and T3=T4->T5 60 | so compose: (T6->T5) * (T4->T6) -> (T4->T5) 61 | now replace unconstrained types /consistently/ with type variables: 62 | ('a -> 'b) * ('c -> 'a) -> ('c -> 'b) 63 | *) 64 | fun compose (f,g) = fn x => f (g x) 65 | 66 | -------------------------------------------------------------------------------- /resources/04code/70_other_inference.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: The Value Restriction and Other Type-Inference Challenges *) 3 | 4 | (* first line is not polymorphic so next two lines do not type-check *) 5 | val r = ref NONE 6 | 7 | (* 8 | val _ = r := SOME "hi" 9 | 10 | val i = 1 + valOf (!r) 11 | *) 12 | 13 | type 'a foo = 'a ref 14 | val f : 'a -> 'a foo = ref 15 | val r2 = f NONE (* also need value restriction here *) 16 | 17 | (* where the value restriction arises despite no mutation *) 18 | val pairWithOne = List.map (fn x => (x,1)) 19 | 20 | (* a workaround *) 21 | fun pairWithOne2 xs = List.map (fn x => (x,1)) xs 22 | 23 | -------------------------------------------------------------------------------- /resources/04code/71_mutual_recursion.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: Mutual Recursion *) 3 | 4 | (* an example of mutual recursion: a little "state machine" for 5 | deciding if a list of ints alternates between 1 and 2, not ending with a 1. 6 | This example is simple, but any finite state machine can be programmed via 7 | a set of mutally recursive functions for the states. 8 | *) 9 | fun match xs = 10 | let fun s_need_one xs = 11 | case xs of 12 | [] => true 13 | | 1::xs' => s_need_two xs' 14 | | _ => false 15 | and s_need_two xs = 16 | case xs of 17 | [] => false 18 | | 2::xs' => s_need_one xs' 19 | | _ => false 20 | in 21 | s_need_one xs 22 | end 23 | 24 | (* mutual recursion works fine in ML provided you can put the functions 25 | "next to each other". Otherwise there are workarounds... 26 | *) 27 | datatype t1 = Foo of int | Bar of t2 28 | and t2 = Baz of string | Quux of t1 29 | 30 | fun no_zeros_or_empty_strings_t1 x = 31 | case x of 32 | Foo i => i <> 0 33 | | Bar y => no_zeros_or_empty_strings_t2 y 34 | and no_zeros_or_empty_strings_t2 x = 35 | case x of 36 | Baz s => size s > 0 37 | | Quux y => no_zeros_or_empty_strings_t1 y 38 | 39 | (* code above works fine. this version works if you cannot put the functions 40 | next to each other for some reason (e.g., different modules) *) 41 | 42 | fun no_zeros_or_empty_strings_t1_alternate(f,x) = 43 | case x of 44 | Foo i => i <> 0 45 | | Bar y => f y 46 | 47 | fun no_zeros_or_empty_string_t2_alternate x = 48 | case x of 49 | Baz s => size s > 0 50 | | Quux y => no_zeros_or_empty_strings_t1_alternate(no_zeros_or_empty_string_t2_alternate,y) 51 | 52 | -------------------------------------------------------------------------------- /resources/04code/72_namespace_mgmt.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: Modules for Namespace Management *) 3 | 4 | structure MyMathLib = 5 | struct 6 | 7 | fun fact x = 8 | if x=0 9 | then 1 10 | else x * fact (x - 1) 11 | 12 | val half_pi = Math.pi / 2.0 13 | 14 | fun doubler y = y + y 15 | 16 | end 17 | 18 | val pi = MyMathLib.half_pi + MyMathLib.half_pi 19 | 20 | val twenty_eight = MyMathLib.doubler 14 21 | -------------------------------------------------------------------------------- /resources/04code/73_signatures.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: Signatures and Hiding Things *) 3 | 4 | signature MATHLIB = 5 | sig 6 | val fact : int -> int 7 | val half_pi : real 8 | (* val doubler : int -> int *) (* can hide bindings from clients *) 9 | end 10 | 11 | structure MyMathLib :> MATHLIB = 12 | struct 13 | fun fact x = 14 | if x=0 15 | then 1 16 | else x * fact (x - 1) 17 | 18 | val half_pi = Math.pi / 2.0 19 | 20 | fun doubler y = y + y 21 | end 22 | 23 | val pi = MyMathLib.half_pi + MyMathLib.half_pi 24 | 25 | val twenty_eight = MyMathLib.doubler 14 26 | -------------------------------------------------------------------------------- /resources/04code/74_module_example.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: A Module Example *) 3 | 4 | (* will assign different signatures to this module in later segments *) 5 | 6 | structure Rational1 = 7 | struct 8 | 9 | (* Invariant 1: all denominators > 0 10 | Invariant 2: rationals kept in reduced form *) 11 | 12 | datatype rational = Whole of int | Frac of int*int 13 | exception BadFrac 14 | 15 | (* gcd and reduce help keep fractions reduced, 16 | but clients need not know about them *) 17 | (* they _assume_ their inputs are not negative *) 18 | fun gcd (x,y) = 19 | if x=y 20 | then x 21 | else if x < y 22 | then gcd(x,y-x) 23 | else gcd(y,x) 24 | 25 | fun reduce r = 26 | case r of 27 | Whole _ => r 28 | | Frac(x,y) => 29 | if x=0 30 | then Whole 0 31 | else let val d = gcd(abs x,y) in (* using invariant 1 *) 32 | if d=y 33 | then Whole(x div d) 34 | else Frac(x div d, y div d) 35 | end 36 | 37 | (* when making a frac, we ban zero denominators *) 38 | fun make_frac (x,y) = 39 | if y = 0 40 | then raise BadFrac 41 | else if y < 0 42 | then reduce(Frac(~x,~y)) 43 | else reduce(Frac(x,y)) 44 | 45 | (* using math properties, both invariants hold of the result 46 | assuming they hold of the arguments *) 47 | fun add (r1,r2) = 48 | case (r1,r2) of 49 | (Whole(i),Whole(j)) => Whole(i+j) 50 | | (Whole(i),Frac(j,k)) => Frac(j+k*i,k) 51 | | (Frac(j,k),Whole(i)) => Frac(j+k*i,k) 52 | | (Frac(a,b),Frac(c,d)) => reduce (Frac(a*d + b*c, b*d)) 53 | 54 | (* given invariant, prints in reduced form *) 55 | fun toString r = 56 | case r of 57 | Whole i => Int.toString i 58 | | Frac(a,b) => (Int.toString a) ^ "/" ^ (Int.toString b) 59 | 60 | end 61 | -------------------------------------------------------------------------------- /resources/04code/75_signatures_for_example.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: Signatures for Our Example *) 3 | 4 | (* this signature hides gcd and reduce. 5 | That way clients cannot assume they exist or 6 | call them with unexpected inputs. *) 7 | signature RATIONAL_A = 8 | sig 9 | datatype rational = Frac of int * int | Whole of int 10 | exception BadFrac 11 | val make_frac : int * int -> rational 12 | val add : rational * rational -> rational 13 | val toString : rational -> string 14 | end 15 | 16 | (* the previous signature lets clients build 17 | any value of type rational they 18 | want by exposing the Frac and Whole constructors. 19 | This makes it impossible to maintain invariants 20 | about rationals, so we might have negative denominators, 21 | which some functions do not handle, 22 | and print_rat may print a non-reduced fraction. 23 | We fix this by making rational abstract. *) 24 | signature RATIONAL_B = 25 | sig 26 | type rational (* type now abstract *) 27 | exception BadFrac 28 | val make_frac : int * int -> rational 29 | val add : rational * rational -> rational 30 | val toString : rational -> string 31 | end 32 | 33 | (* as a cute trick, it is actually okay to expose 34 | the Whole function since no value breaks 35 | our invariants, and different implementations 36 | can still implement Whole differently. 37 | *) 38 | signature RATIONAL_C = 39 | sig 40 | type rational (* type still abstract *) 41 | exception BadFrac 42 | val Whole : int -> rational 43 | (* client knows only that Whole is a function *) 44 | val make_frac : int * int -> rational 45 | val add : rational * rational -> rational 46 | val toString : rational -> string 47 | end 48 | 49 | (* this structure provides a small library 50 | for rational numbers -- same code as in previous 51 | segment, but now we give it a signature *) 52 | structure Rational1 :> RATIONAL_A (*or B or C*) = 53 | struct 54 | (* Invariant 1: all denominators > 0 55 | Invariant 2: rationals kept in reduced form *) 56 | datatype rational = Whole of int | Frac of int*int 57 | exception BadFrac 58 | 59 | (* gcd and reduce help keep fractions reduced, 60 | but clients need not know about them *) 61 | (* they _assume_ their inputs are not negative *) 62 | fun gcd (x,y) = 63 | if x=y 64 | then x 65 | else if x < y 66 | then gcd(x,y-x) 67 | else gcd(y,x) 68 | 69 | fun reduce r = 70 | case r of 71 | Whole _ => r 72 | | Frac(x,y) => 73 | if x=0 74 | then Whole 0 75 | else let val d = gcd(abs x,y) in (* using invariant 1 *) 76 | if d=y 77 | then Whole(x div d) 78 | else Frac(x div d, y div d) 79 | end 80 | 81 | (* when making a frac, we ban zero denominators *) 82 | fun make_frac (x,y) = 83 | if y = 0 84 | then raise BadFrac 85 | else if y < 0 86 | then reduce(Frac(~x,~y)) 87 | else reduce(Frac(x,y)) 88 | 89 | (* using math properties, both invariants hold of the result 90 | assuming they hold of the arguments *) 91 | fun add (r1,r2) = 92 | case (r1,r2) of 93 | (Whole(i),Whole(j)) => Whole(i+j) 94 | | (Whole(i),Frac(j,k)) => Frac(j+k*i,k) 95 | | (Frac(j,k),Whole(i)) => Frac(j+k*i,k) 96 | | (Frac(a,b),Frac(c,d)) => reduce (Frac(a*d + b*c, b*d)) 97 | 98 | (* given invariant, prints in reduced form *) 99 | fun toString r = 100 | case r of 101 | Whole i => Int.toString i 102 | | Frac(a,b) => (Int.toString a) ^ "/" ^ (Int.toString b) 103 | 104 | end 105 | 106 | -------------------------------------------------------------------------------- /resources/04code/77_equivalent_structure.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: An Equivalent Structure *) 3 | 4 | (* this signature hides gcd and reduce. 5 | That way clients cannot assume they exist or 6 | call them with unexpected inputs. *) 7 | signature RATIONAL_A = 8 | sig 9 | datatype rational = Frac of int * int | Whole of int 10 | exception BadFrac 11 | val make_frac : int * int -> rational 12 | val add : rational * rational -> rational 13 | val toString : rational -> string 14 | end 15 | 16 | (* the previous signature lets clients build 17 | any value of type rational they 18 | want by exposing the Frac and Whole constructors. 19 | This makes it impossible to maintain invariants 20 | about rationals, so we might have negative denominators, 21 | which some functions do not handle, 22 | and print_rat may print a non-reduced fraction. 23 | We fix this by making rational abstract. *) 24 | signature RATIONAL_B = 25 | sig 26 | type rational (* type now abstract *) 27 | exception BadFrac 28 | val make_frac : int * int -> rational 29 | val add : rational * rational -> rational 30 | val toString : rational -> string 31 | end 32 | 33 | (* as a cute trick, it is actually okay to expose 34 | the Whole function since no value breaks 35 | our invariants, and different implementations 36 | can still implement Whole differently. 37 | *) 38 | signature RATIONAL_C = 39 | sig 40 | type rational (* type still abstract *) 41 | exception BadFrac 42 | val Whole : int -> rational 43 | (* client knows only that Whole is a function *) 44 | val make_frac : int * int -> rational 45 | val add : rational * rational -> rational 46 | val toString : rational -> string 47 | end 48 | 49 | (* this structure can have all three signatures we gave 50 | Rationa1 in previous segments, and/but it is 51 | /equivalent/ under signatures RATIONAL_B and RATIONAL_C 52 | 53 | this structure does not reduce fractions until printing 54 | *) 55 | 56 | structure Rational2 :> RATIONAL_A (* or B or C *) = 57 | struct 58 | datatype rational = Whole of int | Frac of int*int 59 | exception BadFrac 60 | 61 | fun make_frac (x,y) = 62 | if y = 0 63 | then raise BadFrac 64 | else if y < 0 65 | then Frac(~x,~y) 66 | else Frac(x,y) 67 | 68 | fun add (r1,r2) = 69 | case (r1,r2) of 70 | (Whole(i),Whole(j)) => Whole(i+j) 71 | | (Whole(i),Frac(j,k)) => Frac(j+k*i,k) 72 | | (Frac(j,k),Whole(i)) => Frac(j+k*i,k) 73 | | (Frac(a,b),Frac(c,d)) => Frac(a*d + b*c, b*d) 74 | 75 | fun toString r = 76 | let fun gcd (x,y) = 77 | if x=y 78 | then x 79 | else if x < y 80 | then gcd(x,y-x) 81 | else gcd(y,x) 82 | 83 | fun reduce r = 84 | case r of 85 | Whole _ => r 86 | | Frac(x,y) => 87 | if x=0 88 | then Whole 0 89 | else 90 | let val d = gcd(abs x,y) in 91 | if d=y 92 | then Whole(x div d) 93 | else Frac(x div d, y div d) 94 | end 95 | in 96 | case reduce r of 97 | Whole i => Int.toString i 98 | | Frac(a,b) => (Int.toString a) ^ "/" ^ (Int.toString b) 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /resources/04code/78_another_equivalent_structure.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: Another Equivalent Structure *) 3 | 4 | (* this signature hides gcd and reduce. 5 | That way clients cannot assume they exist or 6 | call them with unexpected inputs. *) 7 | signature RATIONAL_A = 8 | sig 9 | datatype rational = Frac of int * int | Whole of int 10 | exception BadFrac 11 | val make_frac : int * int -> rational 12 | val add : rational * rational -> rational 13 | val toString : rational -> string 14 | end 15 | 16 | (* the previous signature lets clients build 17 | any value of type rational they 18 | want by exposing the Frac and Whole constructors. 19 | This makes it impossible to maintain invariants 20 | about rationals, so we might have negative denominators, 21 | which some functions do not handle, 22 | and print_rat may print a non-reduced fraction. 23 | We fix this by making rational abstract. *) 24 | signature RATIONAL_B = 25 | sig 26 | type rational (* type now abstract *) 27 | exception BadFrac 28 | val make_frac : int * int -> rational 29 | val add : rational * rational -> rational 30 | val toString : rational -> string 31 | end 32 | 33 | (* as a cute trick, it is actually okay to expose 34 | the Whole function since no value breaks 35 | our invariants, and different implementations 36 | can still implement Whole differently. 37 | *) 38 | signature RATIONAL_C = 39 | sig 40 | type rational (* type still abstract *) 41 | exception BadFrac 42 | val Whole : int -> rational 43 | (* client knows only that Whole is a function *) 44 | val make_frac : int * int -> rational 45 | val add : rational * rational -> rational 46 | val toString : rational -> string 47 | end 48 | 49 | (* this structure uses a different abstract type. 50 | It does not even have signature RATIONAL_A. 51 | For RATIONAL_C, we need a function Whole. 52 | *) 53 | structure Rational3 :> RATIONAL_B (* or C *)= 54 | struct 55 | type rational = int * int 56 | exception BadFrac 57 | 58 | fun make_frac (x,y) = 59 | if y = 0 60 | then raise BadFrac 61 | else if y < 0 62 | then (~x,~y) 63 | else (x,y) 64 | 65 | fun Whole i = (i,1) 66 | 67 | fun add ((a,b),(c,d)) = (a*d + c*b, b*d) 68 | 69 | fun toString (x,y) = 70 | if x=0 71 | then "0" 72 | else 73 | let fun gcd (x,y) = 74 | if x=y 75 | then x 76 | else if x < y 77 | then gcd(x,y-x) 78 | else gcd(y,x) 79 | val d = gcd (abs x,y) 80 | val num = x div d 81 | val denom = y div d 82 | in 83 | Int.toString num ^ (if denom=1 84 | then "" 85 | else "/" ^ (Int.toString denom)) 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /resources/04code/79_modules_different_types.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 4: Different Modules Define Different Types *) 3 | 4 | signature RATIONAL_A = 5 | sig 6 | datatype rational = Frac of int * int | Whole of int 7 | exception BadFrac 8 | val make_frac : int * int -> rational 9 | val add : rational * rational -> rational 10 | val toString : rational -> string 11 | end 12 | 13 | signature RATIONAL_B = 14 | sig 15 | type rational (* type now abstract *) 16 | exception BadFrac 17 | val make_frac : int * int -> rational 18 | val add : rational * rational -> rational 19 | val toString : rational -> string 20 | end 21 | 22 | signature RATIONAL_C = 23 | sig 24 | type rational (* type still abstract *) 25 | exception BadFrac 26 | val Whole : int -> rational (* client knows only that Whole is a function *) 27 | val make_frac : int * int -> rational 28 | val add : rational * rational -> rational 29 | val toString : rational -> string 30 | end 31 | 32 | structure Rational1 :> RATIONAL_C = 33 | struct 34 | 35 | (* Invariant 1: all denominators > 0 36 | Invariant 2: rationals kept in reduced form *) 37 | 38 | datatype rational = Whole of int | Frac of int*int 39 | exception BadFrac 40 | 41 | (* gcd and reduce help keep fractions reduced, 42 | but clients need not know about them *) 43 | (* they _assume_ their inputs are not negative *) 44 | fun gcd (x,y) = 45 | if x=y 46 | then x 47 | else if x < y 48 | then gcd(x,y-x) 49 | else gcd(y,x) 50 | 51 | fun reduce r = 52 | case r of 53 | Whole _ => r 54 | | Frac(x,y) => 55 | if x=0 56 | then Whole 0 57 | else let val d = gcd(abs x,y) in (* using invariant 1 *) 58 | if d=y 59 | then Whole(x div d) 60 | else Frac(x div d, y div d) 61 | end 62 | 63 | (* when making a frac, we ban zero denominators *) 64 | fun make_frac (x,y) = 65 | if y = 0 66 | then raise BadFrac 67 | else if y < 0 68 | then reduce(Frac(~x,~y)) 69 | else reduce(Frac(x,y)) 70 | 71 | (* using math properties, both invariants hold of the result 72 | assuming they hold of the arguments *) 73 | fun add (r1,r2) = 74 | case (r1,r2) of 75 | (Whole(i),Whole(j)) => Whole(i+j) 76 | | (Whole(i),Frac(j,k)) => Frac(j+k*i,k) 77 | | (Frac(j,k),Whole(i)) => Frac(j+k*i,k) 78 | | (Frac(a,b),Frac(c,d)) => reduce (Frac(a*d + b*c, b*d)) 79 | 80 | (* given invariant, prints in reduced form *) 81 | fun toString r = 82 | case r of 83 | Whole i => Int.toString i 84 | | Frac(a,b) => (Int.toString a) ^ "/" ^ (Int.toString b) 85 | 86 | end 87 | 88 | structure Rational2 :> RATIONAL_C = 89 | struct 90 | datatype rational = Whole of int | Frac of int*int 91 | exception BadFrac 92 | 93 | fun make_frac (x,y) = 94 | if y = 0 95 | then raise BadFrac 96 | else if y < 0 97 | then Frac(~x,~y) 98 | else Frac(x,y) 99 | 100 | fun add (r1,r2) = 101 | case (r1,r2) of 102 | (Whole(i),Whole(j)) => Whole(i+j) 103 | | (Whole(i),Frac(j,k)) => Frac(j+k*i,k) 104 | | (Frac(j,k),Whole(i)) => Frac(j+k*i,k) 105 | | (Frac(a,b),Frac(c,d)) => Frac(a*d + b*c, b*d) 106 | 107 | fun toString r = 108 | let fun gcd (x,y) = 109 | if x=y 110 | then x 111 | else if x < y 112 | then gcd(x,y-x) 113 | else gcd(y,x) 114 | 115 | fun reduce r = 116 | case r of 117 | Whole _ => r 118 | | Frac(x,y) => 119 | if x=0 120 | then Whole 0 121 | else 122 | let val d = gcd(abs x,y) in 123 | if d=y 124 | then Whole(x div d) 125 | else Frac(x div d, y div d) 126 | end 127 | in 128 | case reduce r of 129 | Whole i => Int.toString i 130 | | Frac(a,b) => (Int.toString a) ^ "/" ^ (Int.toString b) 131 | end 132 | end 133 | 134 | structure Rational3 :> RATIONAL_C = 135 | struct 136 | type rational = int * int 137 | exception BadFrac 138 | 139 | fun make_frac (x,y) = 140 | if y = 0 141 | then raise BadFrac 142 | else if y < 0 143 | then (~x,~y) 144 | else (x,y) 145 | 146 | fun add ((a,b),(c,d)) = (a*d + c*b, b*d) 147 | 148 | fun toString (x,y) = 149 | if x=0 150 | then "0" 151 | else 152 | let fun gcd (x,y) = 153 | if x=y 154 | then x 155 | else if x < y 156 | then gcd(x,y-x) 157 | else gcd(y,x) 158 | val d = gcd (abs x,y) 159 | val num = x div d 160 | val denom = y div d 161 | in 162 | Int.toString num ^ (if denom=1 163 | then "" 164 | else "/" ^ (Int.toString denom)) 165 | end 166 | 167 | fun Whole i = (i,1) 168 | 169 | end 170 | -------------------------------------------------------------------------------- /resources/04summary.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/resources/04summary.pdf -------------------------------------------------------------------------------- /resources/05code/100_memoization.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Memoizaton 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | (define (fibonacci1 x) 9 | (if (or (= x 1) (= x 2)) 10 | 1 11 | (+ (fibonacci1 (- x 1)) 12 | (fibonacci1 (- x 2))))) 13 | 14 | (define (fibonacci2 x) 15 | (letrec ([f (lambda (acc1 acc2 y) 16 | (if (= y x) 17 | (+ acc1 acc2) 18 | (f (+ acc1 acc2) acc1 (+ y 1))))]) 19 | (if (or (= x 1) (= x 2)) 20 | 1 21 | (f 1 1 3)))) 22 | 23 | (define fibonacci3 24 | (letrec([memo null] ; list of pairs (arg . result) 25 | [f (lambda (x) 26 | (let ([ans (assoc x memo)]) 27 | (if ans 28 | (cdr ans) 29 | (let ([new-ans (if (or (= x 1) (= x 2)) 30 | 1 31 | (+ (f (- x 1)) 32 | (f (- x 2))))]) 33 | (begin 34 | (set! memo (cons (cons x new-ans) memo)) 35 | new-ans)))))]) 36 | f)) 37 | -------------------------------------------------------------------------------- /resources/05code/101_macros_intro.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Macros: The Key Points 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | ; You do *not* need to understand these macro definitions 9 | ; (that is a later optional section), but we want to show 10 | ; uses of them 11 | 12 | ;; a cosmetic macro -- adds then, else 13 | (define-syntax my-if 14 | (syntax-rules (then else) 15 | [(my-if e1 then e2 else e3) 16 | (if e1 e2 e3)])) 17 | 18 | ;; a macro to replace an expression with another one 19 | (define-syntax comment-out 20 | (syntax-rules () 21 | [(comment-out ignore instead) instead])) 22 | 23 | ; makes it so users don't write the thunk when using my-delay 24 | (define-syntax my-delay 25 | (syntax-rules () 26 | [(my-delay e) 27 | (mcons #f (lambda () e))])) 28 | 29 | ; some uses 30 | 31 | (define seven (my-if #t then 7 else 14)) 32 | ; SYNTAX ERROR: (define does-not-work (my-if #t then 7 then 9)) 33 | ; SYNTAX ERROR: (define does-not-work (my-if #t then 7 else else)) 34 | 35 | (define no-problem (comment-out (car null) #f)) 36 | 37 | (define x (begin (print "hello") (* 3 4))) 38 | 39 | (define p (my-delay (begin (print "hi") (* 3 4)))) 40 | 41 | (define (my-force th) 42 | (if (mcar th) 43 | (mcdr th) 44 | (begin (set-mcar! th #t) 45 | (set-mcdr! th ((mcdr th))) 46 | (mcdr th)))) 47 | 48 | ; (my-force p) 49 | ; (my-force p) -------------------------------------------------------------------------------- /resources/05code/103_define_syntax.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Optional: Racket Macros with define-syntax 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | ;; a cosmetic macro -- adds then, else 9 | (define-syntax my-if 10 | (syntax-rules (then else) 11 | [(my-if e1 then e2 else e3) 12 | (if e1 e2 e3)])) 13 | 14 | ;; a macro to replace an expression with another one 15 | (define-syntax comment-out 16 | (syntax-rules () 17 | [(comment-out ignore instead) instead])) 18 | 19 | ; makes it so users do *not* write the thunk when using my-delay 20 | (define-syntax my-delay 21 | (syntax-rules () 22 | [(my-delay e) 23 | (mcons #f (lambda () e))])) 24 | 25 | ; this is really bad because it evaluates e multiple times 26 | (define-syntax my-force-macro1 27 | (syntax-rules () 28 | [(my-force e) 29 | (if (mcar e) 30 | (mcdr e) 31 | (begin (set-mcar! e #t) 32 | (set-mcdr! e ((mcdr e))) 33 | (mcdr e)))])) 34 | 35 | ; do *not* do this either because a function is exactly what we want 36 | (define-syntax my-force-macro2 37 | (syntax-rules () 38 | [(my-force e) 39 | (let ([x e]) 40 | (if (mcar x) 41 | (mcdr x) 42 | (begin (set-mcar! x #t) 43 | (set-mcdr! x ((mcdr x))) 44 | (mcdr x))))])) 45 | 46 | ; just do this: 47 | (define (my-force th) 48 | (if (mcar th) 49 | (mcdr th) 50 | (begin (set-mcar! th #t) 51 | (set-mcdr! th ((mcdr th))) 52 | (mcdr th)))) -------------------------------------------------------------------------------- /resources/05code/105_macro_examples.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Optional: More Macro Examples 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | ;; a loop that executes body hi - lo times 9 | ;; notice use of local variables 10 | (define-syntax for 11 | (syntax-rules (to do) 12 | [(for lo to hi do body) 13 | (let ([l lo] 14 | [h hi]) 15 | (letrec ([loop (lambda (it) 16 | (if (> it h) 17 | #t 18 | (begin body (loop (+ it 1)))))]) 19 | (loop l)))])) 20 | 21 | ;; let2 allows up to two local bindings (with let* semantics) with fewer parentheses 22 | ;; than let* 23 | (define-syntax let2 24 | (syntax-rules () 25 | [(let2 () body) 26 | body] 27 | [(let2 (var val) body) 28 | (let ([var val]) body)] 29 | [(let2 (var1 val1 var2 val2) body) 30 | (let ([var1 val1]) 31 | (let ([var2 val2]) 32 | body))])) 33 | 34 | 35 | ;; the special ... lets us take any number of arguments 36 | ;; Note: nothing prevents infinite code generation except 37 | ;; the macro definer being careful 38 | (define-syntax my-let* 39 | (syntax-rules () 40 | [(my-let* () body) 41 | body] 42 | [(my-let* ([var0 val0] 43 | [var-rest val-rest] ...) 44 | body) 45 | (let ([var0 val0]) 46 | (my-let* ([var-rest val-rest] ...) 47 | body))])) 48 | 49 | -------------------------------------------------------------------------------- /resources/05code/83_racket_intro.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Racket Introduction 3 | 4 | ; always make this the first (non-comment, non-blank) line of your file 5 | #lang racket 6 | 7 | ; not needed here, but a workaround so we could write tests in a second file 8 | ; see getting-started-with-Racket instructions for more explanation 9 | (provide (all-defined-out)) 10 | 11 | ; basic definitions 12 | (define s "hello") 13 | -------------------------------------------------------------------------------- /resources/05code/84_racket_basics.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Racket Definitions, Functions, Conditionals 3 | 4 | ; always make this the first (non-comment, non-blank) line of your file 5 | #lang racket 6 | 7 | ; not needed here, but a workaround so we could write tests in a second file 8 | ; see getting-started-with-Racket instructions for more explanation 9 | (provide (all-defined-out)) 10 | 11 | ; basic definitions 12 | (define x 3) 13 | (define y (+ x 2)) ; function call is (e1 e2 ... en): parens matter! 14 | 15 | ; basic function 16 | (define cube1 17 | (lambda (x) 18 | (* x (* x x)))) 19 | 20 | ; many functions, such as *, take a variable number of arguments 21 | (define cube2 22 | (lambda (x) 23 | (* x x x))) 24 | 25 | ; syntactic sugar for function definitions 26 | (define (cube3 x) 27 | (* x x x)) 28 | 29 | ; conditional 30 | (define (pow1 x y) 31 | (if (= y 0) 32 | 1 33 | (* x (pow1 x (- y 1))))) 34 | 35 | ; currying 36 | (define pow2 37 | (lambda (x) 38 | (lambda (y) 39 | (pow1 x y)))) 40 | 41 | ; sugar for currying (fairly new to Racket) 42 | (define ((pow2b x) y) (pow1 x y)) 43 | 44 | (define three-to-the (pow2 3)) 45 | (define eightyone (three-to-the 4)) 46 | (define sixteen ((pow2 2) 4)) ; need exactly these parens 47 | -------------------------------------------------------------------------------- /resources/05code/85_racket_lists.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Racket Lists 3 | 4 | ; always make this the first (non-comment, non-blank) line of your file 5 | #lang racket 6 | 7 | ; not needed here, but a workaround so we could write tests in a second file 8 | ; see getting-started-with-Racket instructions for more explanation 9 | (provide (all-defined-out)) 10 | 11 | ; list processing: null, cons, null?, car, cdr 12 | ; we won't use pattern-matching in Racket 13 | (define (sum xs) 14 | (if (null? xs) 15 | 0 16 | (+ (car xs) (sum (cdr xs))))) 17 | 18 | (define (my-append xs ys) ; same as append already provided 19 | (if (null? xs) 20 | ys 21 | (cons (car xs) (my-append (cdr xs) ys)))) 22 | 23 | (define (my-map f xs) ; same as map already provided 24 | (if (null? xs) 25 | null 26 | (cons (f (car xs)) (my-map f (cdr xs))))) 27 | 28 | (define foo (my-map (lambda (x) (+ x 1)) (cons 3 (cons 4 (cons 5 null))))) 29 | 30 | -------------------------------------------------------------------------------- /resources/05code/87_parens_matter.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Parentheses Matter! (Debugging Practice) 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | ; [first big difference from ML (and Java)] PARENS MATTER!! 9 | 10 | (define (fact n) (if (= n 0) 1 (* n (fact (- n 1))))) 11 | 12 | ; base case calls the function 1 with zero arguments 13 | (define (fact-wrong1 n) (if (= n 0) (1) (* n (fact-wrong1 (- n 1))))) 14 | 15 | ; so why does this work (hint: it's not recursive 16 | ; and there is no type system): 17 | (define (fact-works1b n) (if (= n 0) (1) (* n (fact (- n 1))))) 18 | 19 | ; passing 5 arguments to if: =, n, 0, 1, (* ...) 20 | ; this is bad syntax 21 | ;(define (fact-wrong2 n) (if = n 0 1 (* n (fact-wrong2 (- n 1))))) 22 | 23 | ; calling n with zero arguments and also having an if 24 | ; this is not a legal definition: bad syntax 25 | ;(define fact-wrong3 (n) (if (= n 0) 1 (* n (fact-wrong3 (- n 1))))) 26 | 27 | ; calling multiply with three arguments, which would be fine 28 | ; except the second one is fact-wrong4 29 | (define (fact-wrong4 n) (if (= n 0) 1 (* n fact-wrong4 (- n 1)))) 30 | 31 | ; calling fact-wrong5 with zero arguments, calling result of that 32 | ; with n-1 33 | (define (fact-wrong5 n) (if (= n 0) 1 (* n ((fact-wrong5) (- n 1))))) 34 | 35 | ; treating n as a function of two arguments, passing it * 36 | 37 | (define (fact-wrong6 n) (if (= n 0) 1 (n * (fact-wrong6 (- n 1))))) 38 | 39 | -------------------------------------------------------------------------------- /resources/05code/88_dynamic_typing.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Dynamic Typing 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | ; [second big difference from ML (and Java)] Dynamic Typing!! 9 | 10 | ; dynamic typing: can use values of any type anywhere 11 | ; e.g., a list that holds numbers or other lists 12 | 13 | ; this function sums lists of (numbers or lists of (numbers or ...)), 14 | ; but it does assume it only encounters lists or numbers (else run-time error) 15 | (define (sum1 xs) 16 | (if (null? xs) 17 | 0 18 | (if (number? (car xs)) 19 | (+ (car xs) (sum1 (cdr xs))) 20 | (+ (sum1 (car xs)) (sum1 (cdr xs)))))) 21 | 22 | ; this version does not fail on non-lists -- it treats them as 0 23 | (define (sum2 xs) 24 | (if (null? xs) 25 | 0 26 | (if (number? (car xs)) 27 | (+ (car xs) (sum2 (cdr xs))) 28 | (if (list? (car xs)) 29 | (+ (sum2 (car xs)) (sum2 (cdr xs))) 30 | (sum2 (cdr xs)))))) 31 | -------------------------------------------------------------------------------- /resources/05code/89_cond.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Cond 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | ; sum3 is equivalent to sum1 from previous segment but better style 9 | (define (sum3 xs) 10 | (cond [(null? xs) 0] 11 | [(number? (car xs)) (+ (car xs)(sum3 (cdr xs)))] 12 | [#t (+ (sum3 (car xs)) (sum3 (cdr xs)))])) 13 | 14 | ; sum4 is equivalent to sum2 from previous segment but better style 15 | (define (sum4 xs) 16 | (cond [(null? xs) 0] 17 | [(number? xs) xs] 18 | [(list? (car xs)) (+ (sum4 (car xs)) (sum4 (cdr xs)))] 19 | [#t (sum4 (cdr xs))])) 20 | 21 | ; this function counts how many #f are in a (non-nested) list 22 | ; it uses the "controversial" idiom of anything not #f is true 23 | (define (count-falses xs) 24 | (cond [(null? xs) 0] 25 | [(car xs) (count-falses (cdr xs))] ; (car xs) can have any type 26 | [#t (+ 1 (count-falses (cdr xs)))])) 27 | -------------------------------------------------------------------------------- /resources/05code/90_local_bindings.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Local Bindings 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | ; notice the parentheses in the let-expressions 9 | (define (max-of-list xs) 10 | (cond [(null? xs) (error "max-of-list given empty list")] 11 | [(null? (cdr xs)) (car xs)] 12 | [#t (let ([tlans (max-of-list (cdr xs))]) 13 | (if (> tlans (car xs)) 14 | tlans 15 | (car xs)))])) 16 | 17 | ; 4 forms of local bindings 18 | 19 | ; let evaluates all expressions using outer environment, 20 | ; *not* earlier bindings 21 | (define (double1 x) 22 | (let ([x (+ x 3)] 23 | [y (+ x 2)]) 24 | (+ x y -5))) 25 | 26 | ; let* is like ML's let: environment includes previous bindings 27 | (define (double2 x) 28 | (let* ([x (+ x 3)] 29 | [y (+ x 2)]) 30 | (+ x y -8))) 31 | 32 | ; letrec uses an environment where all bindings in scope 33 | ; * like ML's use of and for mutual recursion 34 | ; * you get an error if you use a variable before it's defined 35 | ; where as always function bodies not used until called 36 | ; (bindings still evaluated in order) 37 | ; (In versions of Racket before 6.1, you got an # value instead, 38 | ; which would typically cause an error as soon as you used it.) 39 | (define (triple x) 40 | (letrec ([y (+ x 2)] 41 | [f (lambda (z) (+ z y w x))] 42 | [w (+ x 7)]) 43 | (f -9))) 44 | 45 | (define (mod2 x) 46 | (letrec 47 | ([even?(lambda (x) (if (zero? x) #t (odd? (- x 1))))] 48 | [odd? (lambda (x) (if (zero? x) #f (even? (- x 1))))]) 49 | (if (even? x) 0 1))) 50 | 51 | (define (bad-letrec-example x) 52 | (letrec ([y z] ; okay to be a lambda that uses z, but here y undefined 53 | [z 13]) 54 | (if x y z))) 55 | 56 | ; and you can use define locally (in some positions) 57 | ; the same as letrec when binding local variables 58 | (define (mod2_b x) 59 | (define even? (lambda(x)(if (zero? x) #t (odd? (- x 1))))) 60 | (define odd? (lambda(x)(if (zero? x) #f (even? (- x 1))))) 61 | (if (even? x) 0 1)) 62 | 63 | -------------------------------------------------------------------------------- /resources/05code/91_toplevel_bindings.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Top-Level Bindings 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | ; at the top-level (*) 9 | ; same letrec-like rules: can have forward references, but 10 | ; definitions still evaluate in order and cannot be repeated 11 | 12 | (define (f x) (+ x (* x b))) ; forward reference okay here 13 | (define b 3) 14 | (define c (+ b 4)) ; backward reference okay 15 | ;(define d (+ e 4)) ; not okay (get an error) 16 | (define e 5) 17 | ;(define f 17) ; not okay: f already defined in this module 18 | 19 | ; (*) we are not actually at top-level -- we are in a module called 91_toplevel_bindings -------------------------------------------------------------------------------- /resources/05code/92_setbang.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Mutation With set! 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | (define b 3) 9 | (define f (lambda (x) (* 1 (+ x b)))) 10 | (define c (+ b 4)) 11 | (set! b 5) 12 | (define z (f 4)) 13 | (define w c) 14 | 15 | ; a safe version of f would make a local copy of b 16 | ; no need to call the local varaible b also, but no reason not to either 17 | (define f 18 | (let ([b b]) 19 | (lambda (x) (* 1 (+ x b))))) 20 | 21 | ; but maybe that is not good enough since + and * are also procedures 22 | (define f 23 | (let ([b b] 24 | [+ +] 25 | [* +]) 26 | (lambda (x) (* 1 (+ x b))))) 27 | 28 | ; it turns out: 29 | ; 1. this technique doesn't work if f calls a function that itself calls 30 | ; things that might get mutated 31 | ; 2. we don't have to worry about this in Racket because +, * are immutable: 32 | ; the general rule is a top-level binding is mutable only if /that/ module 33 | ; contains a set! of it -------------------------------------------------------------------------------- /resources/05code/93_truth_about_cons.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: The Truth About Cons 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | (define pr (cons 1 (cons #t "hi"))) 9 | (define lst (cons 1 (cons #t (cons "hi" null)))) 10 | (define hi (cdr (cdr pr))) 11 | (define hi-again (car (cdr (cdr lst)))) 12 | (define hi-again-shorter (caddr lst)) 13 | (define no (list? pr)) 14 | (define yes (pair? pr)) 15 | (define of-course (and (list? lst) (pair? lst))) 16 | ; (define do-not-do-this (length pr)) 17 | 18 | -------------------------------------------------------------------------------- /resources/05code/94_mcons.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: mcons 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | ; cons cells are immutable -- this does not change a cell's contents 9 | (define x (cons 14 null)) 10 | (define y x) 11 | (set! x (cons 42 null)) 12 | (define fourteen (car y)) 13 | 14 | ; but since mutable pairs are useful, Racket has them too: 15 | ; mcons, mcar, mcdr, set-mcar!, set-mcdr! 16 | (define mpr (mcons 1 (mcons #t "hi"))) 17 | (set-mcdr! (mcdr mpr) "bye") 18 | (define bye (mcdr (mcdr mpr))) 19 | 20 | ; Note: run-time error to use mcar on a cons or car on an mcons -------------------------------------------------------------------------------- /resources/05code/95_thunks.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Delayed Evaluation and Thunks 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | (define (factorial-normal x) 9 | (if (= x 0) 10 | 1 11 | (* x (factorial-normal (- x 1))))) 12 | 13 | (define (my-if-bad e1 e2 e3) 14 | (if e1 e2 e3)) 15 | 16 | (define (factorial-bad x) 17 | (my-if-bad (= x 0) 18 | 1 19 | (* x 20 | (factorial-bad (- x 1))))) 21 | 22 | (define (my-if-strange-but-works e1 e2 e3) 23 | (if e1 (e2) (e3))) 24 | 25 | (define (factorial-okay x) 26 | (my-if-strange-but-works (= x 0) 27 | (lambda () 1) 28 | (lambda () (* x (factorial-okay (- x 1)))))) 29 | -------------------------------------------------------------------------------- /resources/05code/96_avoid_unnecessary.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Avoiding Unnecessary Computations: Delay and Force 3 | 4 | ; [this is the code for two segments related to thunking and delay/force] 5 | 6 | #lang racket 7 | 8 | (provide (all-defined-out)) 9 | 10 | ; this is a silly addition function that purposely runs slows for 11 | ; demonstration purposes 12 | (define (slow-add x y) 13 | (letrec ([slow-id (lambda (y z) 14 | (if (= 0 z) 15 | y 16 | (slow-id y (- z 1))))]) 17 | (+ (slow-id x 50000000) y))) 18 | 19 | ; multiplies x and result of y-thunk, calling y-thunk x times 20 | (define (my-mult x y-thunk) ;; assumes x is >= 0 21 | (cond [(= x 0) 0] 22 | [(= x 1) (y-thunk)] 23 | [#t (+ (y-thunk) (my-mult (- x 1) y-thunk))])) 24 | 25 | ; these calls: great for 0, okay for 1, bad for > 1 26 | ;(my-mult 0 (lambda () (slow-add 3 4))) 27 | ;(my-mult 1 (lambda () (slow-add 3 4))) 28 | ;(my-mult 2 (lambda () (slow-add 3 4))) 29 | 30 | ; these calls: okay for all 31 | ;(my-mult 0 (let ([x (slow-add 3 4)]) (lambda () x))) 32 | ;(my-mult 1 (let ([x (slow-add 3 4)]) (lambda () x))) 33 | ;(my-mult 2 (let ([x (slow-add 3 4)]) (lambda () x))) 34 | 35 | (define (my-delay th) 36 | (mcons #f th)) ;; a one-of "type" we will update /in place/ 37 | 38 | (define (my-force p) 39 | (if (mcar p) 40 | (mcdr p) 41 | (begin (set-mcar! p #t) 42 | (set-mcdr! p ((mcdr p))) 43 | (mcdr p)))) 44 | 45 | ; these calls: great for 0, okay for 1, okay for > 1 46 | ;(my-mult 0 (let ([x (my-delay (lambda () (slow-add 3 4)))]) (lambda () (my-force x)))) 47 | ;(my-mult 1 (let ([x (my-delay (lambda () (slow-add 3 4)))]) (lambda () (my-force x)))) 48 | ;(my-mult 2 (let ([x (my-delay (lambda () (slow-add 3 4)))]) (lambda () (my-force x)))) 49 | -------------------------------------------------------------------------------- /resources/05code/97_delay_force.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Avoiding Unnecessary Computations: Delay and Force 3 | 4 | ; [this is the code for two segments related to thunking and delay/force] 5 | 6 | #lang racket 7 | 8 | (provide (all-defined-out)) 9 | 10 | ; this is a silly addition function that purposely runs slows for 11 | ; demonstration purposes 12 | (define (slow-add x y) 13 | (letrec ([slow-id (lambda (y z) 14 | (if (= 0 z) 15 | y 16 | (slow-id y (- z 1))))]) 17 | (+ (slow-id x 50000000) y))) 18 | 19 | ; multiplies x and result of y-thunk, calling y-thunk x times 20 | (define (my-mult x y-thunk) ;; assumes x is >= 0 21 | (cond [(= x 0) 0] 22 | [(= x 1) (y-thunk)] 23 | [#t (+ (y-thunk) (my-mult (- x 1) y-thunk))])) 24 | 25 | ;(my-mult 0 (lambda () (slow-add 3 4))) 26 | ;(my-mult 1 (lambda () (slow-add 3 4))) 27 | ;(my-mult 2 (lambda () (slow-add 3 4))) 28 | ;(my-mult 0 (let ([x (slow-add 3 4)]) (lambda () x))) 29 | ;(my-mult 1 (let ([x (slow-add 3 4)]) (lambda () x))) 30 | ;(my-mult 2 (let ([x (slow-add 3 4)]) (lambda () x))) 31 | 32 | (define (my-delay th) 33 | (mcons #f th)) ;; a one-of "type" we will update /in place/ 34 | 35 | (define (my-force p) 36 | (if (mcar p) 37 | (mcdr p) 38 | (begin (set-mcar! p #t) 39 | (set-mcdr! p ((mcdr p))) 40 | (mcdr p)))) 41 | 42 | ;(my-mult 0 (let ([x (my-delay (lambda () (slow-add 3 4)))]) (lambda () (my-force x)))) 43 | ;(my-mult 1 (let ([x (my-delay (lambda () (slow-add 3 4)))]) (lambda () (my-force x)))) 44 | ;(my-mult 2 (let ([x (my-delay (lambda () (slow-add 3 4)))]) (lambda () (my-force x)))) 45 | -------------------------------------------------------------------------------- /resources/05code/98_using_streams.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Using and Defining Streams 3 | 4 | ; [same code for segments on using streams and defining streams] 5 | 6 | #lang racket 7 | 8 | (provide (all-defined-out)) 9 | 10 | ;; define some streams 11 | 12 | ;(define ones-really-bad (cons 1 ones-really-bad)) 13 | (define ones-bad (lambda () (cons 1 (ones-bad)))) 14 | 15 | (define ones (lambda () (cons 1 ones))) 16 | 17 | (define nats 18 | (letrec ([f (lambda (x) (cons x (lambda () (f (+ x 1)))))]) 19 | (lambda () (f 1)))) 20 | 21 | (define powers-of-two 22 | (letrec ([f (lambda (x) (cons x (lambda () (f (* x 2)))))]) 23 | (lambda () (f 2)))) 24 | 25 | (define (stream-maker fn arg) 26 | (letrec ([f (lambda (x) 27 | (cons x (lambda () (f (fn x arg)))))]) 28 | (lambda () (f arg)))) 29 | (define nats2 (stream-maker + 1)) 30 | (define powers2 (stream-maker * 2)) 31 | 32 | ;; code that uses streams 33 | 34 | (define (number-until stream tester) 35 | (letrec ([f (lambda (stream ans) 36 | (let ([pr (stream)]) 37 | (if (tester (car pr)) 38 | ans 39 | (f (cdr pr) (+ ans 1)))))]) 40 | (f stream 1))) 41 | 42 | (define four (number-until powers-of-two (lambda (x) (= x 16)))) 43 | -------------------------------------------------------------------------------- /resources/05code/99_defining_streams.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 5: Using and Defining Streams 3 | 4 | ; [same code for segments on using streams and defining streams] 5 | 6 | #lang racket 7 | 8 | (provide (all-defined-out)) 9 | 10 | ;; define some streams 11 | 12 | ;(define ones-really-bad (cons 1 ones-really-bad)) 13 | (define ones-bad (lambda () (cons 1 (ones-bad)))) 14 | 15 | (define ones (lambda () (cons 1 ones))) 16 | 17 | (define nats 18 | (letrec ([f (lambda (x) (cons x (lambda () (f (+ x 1)))))]) 19 | (lambda () (f 1)))) 20 | 21 | (define powers-of-two 22 | (letrec ([f (lambda (x) (cons x (lambda () (f (* x 2)))))]) 23 | (lambda () (f 2)))) 24 | 25 | (define (stream-maker fn arg) 26 | (letrec ([f (lambda (x) 27 | (cons x (lambda () (f (fn x arg)))))]) 28 | (lambda () (f arg)))) 29 | (define nats2 (stream-maker + 1)) 30 | (define powers2 (stream-maker * 2)) 31 | 32 | ;; code that uses streams 33 | 34 | (define (number-until stream tester) 35 | (letrec ([f (lambda (stream ans) 36 | (let ([pr (stream)]) 37 | (if (tester (car pr)) 38 | ans 39 | (f (cdr pr) (+ ans 1)))))]) 40 | (f stream 1))) 41 | 42 | (define four (number-until powers-of-two (lambda (x) (= x 16)))) 43 | -------------------------------------------------------------------------------- /resources/05summary.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/resources/05summary.pdf -------------------------------------------------------------------------------- /resources/06code/107_datatypes_without_structs.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 6: Datatype Programming in Racket Without Structs 3 | 4 | #lang racket 5 | (provide (all-defined-out)) 6 | 7 | ; we do not need a datatype to create a list holding a mix of numbers and string 8 | ; Note: arguably bad style to not have else clause but makes example more like ML code 9 | (define (funny-sum xs) 10 | (cond [(null? xs) 0] 11 | [(number? (car xs)) (+ (car xs) (funny-sum (cdr xs)))] 12 | [(string? (car xs)) (+ (string-length (car xs)) (funny-sum (cdr xs)))])) 13 | 14 | ; suppose we wanted a "one-of type" using nothing but lists and symbols and such, like ML's 15 | ; datatype exp = Const of int | Negate of exp | Add of exp * exp | Multiply of exp * exp 16 | 17 | ; just helper functions that make lists where first element is a symbol 18 | ; Note: More robust could check at run-time the type of thing being put in 19 | (define (Const i) (list 'Const i)) 20 | (define (Negate e) (list 'Negate e)) 21 | (define (Add e1 e2) (list 'Add e1 e2)) 22 | (define (Multiply e1 e2) (list 'Multiply e1 e2)) 23 | 24 | ; just helper functions that test what "kind of exp" 25 | ; Note: More robust could raise better errors for non-exp values 26 | (define (Const? x) (eq? (car x) 'Const)) 27 | (define (Negate? x) (eq? (car x) 'Negate)) 28 | (define (Add? x) (eq? (car x) 'Add)) 29 | (define (Multiply? x) (eq? (car x) 'Multiply)) 30 | 31 | ; just helper functions that get the pieces for "one kind of exp" 32 | ; Note: More robust could check "what kind of exp" 33 | (define (Const-int e) (car (cdr e))) 34 | (define (Negate-e e) (car (cdr e))) 35 | (define (Add-e1 e) (car (cdr e))) 36 | (define (Add-e2 e) (car (cdr (cdr e)))) 37 | (define (Multiply-e1 e) (car (cdr e))) 38 | (define (Multiply-e2 e) (car (cdr (cdr e)))) 39 | 40 | ; fyi: there are built-in functions for getting 2nd, 3rd list elements that would 41 | ; have made the above simpler: 42 | ;(define Const-int cadr) 43 | ;(define Negate-e cadr) 44 | ;(define Add-e1 cadr) 45 | ;(define Add-e2 caddr) 46 | ;(define Multiply-e1 cadr) 47 | ;(define Multiply-e2 caddr) 48 | 49 | ; same recursive structure as we have in ML, just without pattern-matching 50 | ; And one change from what we did before: returning an exp, in particular 51 | ; a Constant, rather than an int 52 | (define (eval-exp e) 53 | (cond [(Const? e) e] ; note returning an exp, not a number 54 | [(Negate? e) (Const (- (Const-int (eval-exp (Negate-e e)))))] 55 | [(Add? e) (let ([v1 (Const-int (eval-exp (Add-e1 e)))] 56 | [v2 (Const-int (eval-exp (Add-e2 e)))]) 57 | (Const (+ v1 v2)))] 58 | [(Multiply? e) (let ([v1 (Const-int (eval-exp (Multiply-e1 e)))] 59 | [v2 (Const-int (eval-exp (Multiply-e2 e)))]) 60 | (Const (* v1 v2)))] 61 | [#t (error "eval-exp expected an exp")])) 62 | 63 | (define a-test (eval-exp (Multiply (Negate (Add (Const 2) (Const 2))) (Const 7)))) -------------------------------------------------------------------------------- /resources/06code/107_datatypes_without_structs.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 6: Datatype Programming in Racket Without Structs *) 3 | 4 | datatype int_or_string = I of int | S of string 5 | 6 | fun funny_sum xs = 7 | case xs of 8 | [] => 0 9 | | (I i)::xs' => i + funny_sum xs' 10 | | (S s)::xs' => String.size s + funny_sum xs' 11 | 12 | datatype exp = Const of int 13 | | Negate of exp 14 | | Add of exp * exp 15 | | Multiply of exp * exp 16 | 17 | (* exp -> int *) 18 | fun eval_exp_old e = 19 | case e of 20 | Const i => i 21 | | Negate e2 => ~ (eval_exp_old e2) 22 | | Add(e1,e2) => (eval_exp_old e1) + (eval_exp_old e2) 23 | | Multiply(e1,e2) => (eval_exp_old e1) * (eval_exp_old e2) 24 | 25 | (* exp -> exp *) 26 | (* this way is more painful here, but makes much more sense for 27 | larger languages where recursive calls can return different kinds of 28 | things (e.g., numbers or pairs or functions or ... ) 29 | *) 30 | exception Error of string 31 | 32 | fun eval_exp_new e = 33 | let 34 | fun get_int e = 35 | case e of 36 | Const i => i 37 | | _ => raise (Error "expected Const result") 38 | in 39 | case e of 40 | Const _ => 41 | e 42 | | Negate e2 => 43 | Const (~ (get_int (eval_exp_new e2))) 44 | | Add(e1,e2) => 45 | Const ((get_int (eval_exp_new e1)) + (get_int (eval_exp_new e2))) 46 | | Multiply(e1,e2) => 47 | Const ((get_int (eval_exp_new e1)) * (get_int (eval_exp_new e2))) 48 | end 49 | 50 | val test_exp = Multiply (Negate (Add (Const 2,Const 2)), Const 7) 51 | val old_test = eval_exp_old test_exp (* ~28 *) 52 | val new_test = eval_exp_new test_exp (* Const ~28 *) 53 | 54 | -------------------------------------------------------------------------------- /resources/06code/108_datatypes_with_structs.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 6: Datatypes With Struct 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | (struct const (int) #:transparent) 9 | (struct negate (e) #:transparent) 10 | (struct add (e1 e2) #:transparent) 11 | (struct multiply (e1 e2) #:transparent) 12 | 13 | (define (eval-exp e) 14 | (cond [(const? e) e] ; note returning an exp, not a number 15 | [(negate? e) (const (- (const-int (eval-exp (negate-e e)))))] 16 | [(add? e) (let ([v1 (const-int (eval-exp (add-e1 e)))] 17 | [v2 (const-int (eval-exp (add-e2 e)))]) 18 | (const (+ v1 v2)))] 19 | [(multiply? e) (let ([v1 (const-int (eval-exp (multiply-e1 e)))] 20 | [v2 (const-int (eval-exp (multiply-e2 e)))]) 21 | (const (* v1 v2)))] 22 | [#t (error "eval-exp expected an exp")])) 23 | 24 | (define a-test (eval-exp (multiply (negate (add (const 2) (const 2))) (const 7)))) -------------------------------------------------------------------------------- /resources/06code/111_interpreter_assumptions.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 6: What Your Interpreter Can and Cannot Assume 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | ; a larger language with two kinds of values, booleans and numbers 9 | ; an expression is any of these: 10 | (struct const (int) #:transparent) ; int should hold a number 11 | (struct negate (e1) #:transparent) ; e1 should hold an expression 12 | (struct add (e1 e2) #:transparent) ; e1, e2 should hold expressions 13 | (struct multiply (e1 e2) #:transparent) ; e1, e2 should hold expressions 14 | (struct bool (b) #:transparent) ; b should hold #t or #f 15 | (struct eq-num (e1 e2) #:transparent) ; e1, e2 should hold expressions 16 | (struct if-then-else (e1 e2 e3) #:transparent) ; e1, e2, e3 should hold expressions 17 | ; a value in this language is a legal const or bool 18 | 19 | (define test1 (multiply (negate (add (const 2) 20 | (const 2))) 21 | (const 7))) 22 | 23 | (define test2 (multiply (negate (add (const 2) 24 | (const 2))) 25 | (if-then-else (bool #f) 26 | (const 7) 27 | (bool #t)))) 28 | 29 | (define non-test (multiply (negate (add (const #t) 30 | (const 2))) 31 | (const 7))) 32 | 33 | (define (eval-exp-wrong e) 34 | (cond [(const? e) 35 | e] 36 | [(negate? e) 37 | (const (- (const-int (eval-exp-wrong (negate-e1 e)))))] 38 | [(add? e) 39 | (let ([i1 (const-int (eval-exp-wrong (add-e1 e)))] 40 | [i2 (const-int (eval-exp-wrong (add-e2 e)))]) 41 | (const (+ i1 i2)))] 42 | [(multiply? e) 43 | (let ([i1 (const-int (eval-exp-wrong (multiply-e1 e)))] 44 | [i2 (const-int (eval-exp-wrong (multiply-e2 e)))]) 45 | (const (* i1 i2)))] 46 | [(bool? e) 47 | e] 48 | [(eq-num? e) 49 | (let ([i1 (const-int (eval-exp-wrong (eq-num-e1 e)))] 50 | [i2 (const-int (eval-exp-wrong (eq-num-e2 e)))]) 51 | (bool (= i1 i2)))] ; creates (bool #t) or (bool #f) 52 | [(if-then-else? e) 53 | (if (bool-b (eval-exp-wrong (if-then-else-e1 e))) 54 | (eval-exp-wrong (if-then-else-e2 e)) 55 | (eval-exp-wrong (if-then-else-e3 e)))] 56 | [#t (error "eval-exp expected an exp")] ; not strictly necessary but helps debugging 57 | )) 58 | 59 | (define (eval-exp e) 60 | (cond [(const? e) 61 | e] 62 | [(negate? e) 63 | (let ([v (eval-exp (negate-e1 e))]) 64 | (if (const? v) 65 | (const (- (const-int v))) 66 | (error "negate applied to non-number")))] 67 | [(add? e) 68 | (let ([v1 (eval-exp (add-e1 e))] 69 | [v2 (eval-exp (add-e2 e))]) 70 | (if (and (const? v1) (const? v2)) 71 | (const (+ (const-int v1) (const-int v2))) 72 | (error "add applied to non-number")))] 73 | [(multiply? e) 74 | (let ([v1 (eval-exp (multiply-e1 e))] 75 | [v2 (eval-exp (multiply-e2 e))]) 76 | (if (and (const? v1) (const? v2)) 77 | (const (* (const-int v1) (const-int v2))) 78 | ((error "multiply applied to non-number"))))] 79 | [(bool? e) 80 | e] 81 | [(eq-num? e) 82 | (let ([v1 (eval-exp (eq-num-e1 e))] 83 | [v2 (eval-exp (eq-num-e2 e))]) 84 | (if (and (const? v1) (const? v2)) 85 | (bool (= (const-int v1) (const-int v2))) ; creates (bool #t) or (bool #f) 86 | (error "eq-num applied to non-number")))] 87 | [(if-then-else? e) 88 | (let ([v-test (eval-exp (if-then-else-e1 e))]) 89 | (if (bool? v-test) 90 | (if (bool-b v-test) 91 | (eval-exp (if-then-else-e2 e)) 92 | (eval-exp (if-then-else-e3 e))) 93 | (error "if-then-else applied to non-boolean")))] 94 | [#t (error "eval-exp expected an exp")] ; not strictly necessary but helps debugging 95 | )) 96 | -------------------------------------------------------------------------------- /resources/06code/115_macros_via_functions.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 6: Using Racket Functions to Add "Macros" to a Language 3 | 4 | #lang racket 5 | 6 | (provide (all-defined-out)) 7 | 8 | ; we start with the same language and interpreter from 9 | ; "What Your Interpreter Can and Cannot Assume" 10 | 11 | ; the new stuff are the "macros" as the bottom 12 | 13 | ; a larger language with two kinds of values, booleans and numbers 14 | ; an expression is any of these: 15 | (struct const (int) #:transparent) ; int should hold a number 16 | (struct negate (e1) #:transparent) ; e1 should hold an expression 17 | (struct add (e1 e2) #:transparent) ; e1, e2 should hold expressions 18 | (struct multiply (e1 e2) #:transparent) ; e1, e2 should hold expressions 19 | (struct bool (b) #:transparent) ; b should hold #t or #f 20 | (struct eq-num (e1 e2) #:transparent) ; e1, e2 should hold expressions 21 | (struct if-then-else (e1 e2 e3) #:transparent) ; e1, e2, e3 should hold expressions 22 | ; a value in this language is a legal const or bool 23 | 24 | (define (eval-exp e) 25 | (cond [(const? e) 26 | e] 27 | [(negate? e) 28 | (let ([v (eval-exp (negate-e1 e))]) 29 | (if (const? v) 30 | (const (- (const-int v))) 31 | (error "negate applied to non-number")))] 32 | [(add? e) 33 | (let ([v1 (eval-exp (add-e1 e))] 34 | [v2 (eval-exp (add-e2 e))]) 35 | (if (and (const? v1) (const? v2)) 36 | (const (+ (const-int v1) (const-int v2))) 37 | (error "add applied to non-number")))] 38 | [(multiply? e) 39 | (let ([v1 (eval-exp (multiply-e1 e))] 40 | [v2 (eval-exp (multiply-e2 e))]) 41 | (if (and (const? v1) (const? v2)) 42 | (const (* (const-int v1) (const-int v2))) 43 | (error "multiply applied to non-number")))] 44 | [(bool? e) 45 | e] 46 | [(eq-num? e) 47 | (let ([v1 (eval-exp (eq-num-e1 e))] 48 | [v2 (eval-exp (eq-num-e2 e))]) 49 | (if (and (const? v1) (const? v2)) 50 | (bool (= (const-int v1) (const-int v2))) ; creates (bool #t) or (bool #f) 51 | (error "eq-num applied to non-number")))] 52 | [(if-then-else? e) 53 | (let ([v-test (eval-exp (if-then-else-e1 e))]) 54 | (if (bool? v-test) 55 | (if (bool-b v-test) 56 | (eval-exp (if-then-else-e2 e)) 57 | (eval-exp (if-then-else-e3 e))) 58 | (error "if-then-else applied to non-boolean")))] 59 | [#t (error "eval-exp expected an exp")] ; not strictly necessary but helps debugging 60 | )) 61 | 62 | ; Here are two Racket functions that given language-being-implemented syntax, 63 | ; produce language-being-implemented syntax 64 | (define (andalso e1 e2) 65 | (if-then-else e1 e2 (bool #f))) 66 | 67 | (define (double e) 68 | (multiply e (const 2))) 69 | 70 | ; this one takes a Racket list of language-being-implemented syntax 71 | ; and makes language-being-implemented syntax 72 | (define (list-product es) 73 | (if (null? es) 74 | (const 1) 75 | (multiply (car es) (list-product (cdr es))))) 76 | 77 | (define test (andalso (eq-num (double (const 4)) 78 | (list-product (list (const 2) (const 2) (const 1) (const 2)))) 79 | (bool #t))) 80 | 81 | ; notice we have not changed our interpreter at all 82 | (define result (eval-exp test)) 83 | -------------------------------------------------------------------------------- /resources/06code/_523523785cf7a0b9a3aa908e8d0345c7_section7_video_code_files/120_static_vs_dynamic_one.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 6: Static Versus Dyanamic Typing, Part 1 3 | 4 | #lang racket 5 | 6 | (define (f y) 7 | (if (> y 0) (+ y y) "hi")) 8 | 9 | (define a (let ([ans (f 7)]) 10 | (if (number? ans) (number->string ans) ans))) 11 | 12 | (define (cube x) 13 | (if (not (number? x)) 14 | (error "bad arguments") 15 | (* x x x))) 16 | 17 | (define b (cube 7)) 18 | 19 | (define (f2 g) 20 | (cons (g 7) (g #t))) 21 | 22 | (define pair_of_pairs 23 | (f2 (lambda (x) (cons x x)))) 24 | 25 | (define (pow-bad-type x) ; curried 26 | (lambda (y) 27 | (if (= y 0) 28 | 1 29 | (* x (pow-bad-type x (- y 1)))))) ; oops 30 | 31 | (define (pow-bad-algorithm x) ; curried 32 | (lambda (y) 33 | (if (= y 0) 34 | 1 35 | (+ x ((pow-bad-algorithm x) (- y 1)))))) ; oops 36 | -------------------------------------------------------------------------------- /resources/06code/_523523785cf7a0b9a3aa908e8d0345c7_section7_video_code_files/120_static_vs_dynamic_one.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 6: Static Versus Dyanamic Typing, Part 1 *) 3 | 4 | datatype t = Int of int | String of string 5 | fun f y = if y > 0 then Int(y+y) else String "hi" 6 | 7 | fun foo x = case f x of 8 | Int i => Int.toString i 9 | | String s => s 10 | 11 | fun cube x = x * x * x 12 | 13 | val z = cube 7 14 | 15 | (* fun f g = (g 7, g true) *) (* does not type-check *) 16 | (* val pair_of_pairs = f (fn x => (x,x)) *) 17 | 18 | datatype tort = Int of int 19 | | String of string 20 | | Cons of tort * tort 21 | | Fun of tort -> tort 22 | (* would have more constructors *) 23 | 24 | val _ = if true 25 | then Fun (fn x => case x of Int i => Int (i*i*i)) 26 | else Cons (Int 7, String "hi") 27 | 28 | (* does not type-check *) 29 | (* 30 | fun pow x y = 31 | if y = 0 32 | then 1 33 | else x * pow (x,y-1) 34 | *) 35 | 36 | (* wrong algorithm *) 37 | fun pow x y = (* curried *) 38 | if y = 0 39 | then 1 40 | else x + pow x (y-1) (* oops *) 41 | -------------------------------------------------------------------------------- /resources/06code/_523523785cf7a0b9a3aa908e8d0345c7_section7_video_code_files/122_eval.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 6: Optional: eval and quote 3 | 4 | #lang racket 5 | 6 | (define (make-some-code1 y) ; just returns a list 7 | (if y 8 | (list 'begin (list 'print "hi") (list '+ 4 2)) 9 | (list '+ 5 3))) 10 | 11 | (define (make-some-code2 y) ; same as make-some-code1 12 | (if y 13 | (quote (begin (print "hi") (+ 4 2))) 14 | (quote (+ 5 3)))) 15 | 16 | (define (test1) (eval (make-some-code1 #t))) ; prints "hi", result 6 17 | (define (test2) (eval (make-some-code1 #t))) ; prints "hi", result 6 18 | 19 | -------------------------------------------------------------------------------- /resources/06summary.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/resources/06summary.pdf -------------------------------------------------------------------------------- /resources/07code/120_static_vs_dynamic_one.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 6: Static Versus Dyanamic Typing, Part 1 3 | 4 | #lang racket 5 | 6 | (define (f y) 7 | (if (> y 0) (+ y y) "hi")) 8 | 9 | (define a (let ([ans (f 7)]) 10 | (if (number? ans) (number->string ans) ans))) 11 | 12 | (define (cube x) 13 | (if (not (number? x)) 14 | (error "bad arguments") 15 | (* x x x))) 16 | 17 | (define b (cube 7)) 18 | 19 | (define (f2 g) 20 | (cons (g 7) (g #t))) 21 | 22 | (define pair_of_pairs 23 | (f2 (lambda (x) (cons x x)))) 24 | 25 | (define (pow-bad-type x) ; curried 26 | (lambda (y) 27 | (if (= y 0) 28 | 1 29 | (* x (pow-bad-type x (- y 1)))))) ; oops 30 | 31 | (define (pow-bad-algorithm x) ; curried 32 | (lambda (y) 33 | (if (= y 0) 34 | 1 35 | (+ x ((pow-bad-algorithm x) (- y 1)))))) ; oops 36 | -------------------------------------------------------------------------------- /resources/07code/120_static_vs_dynamic_one.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 6: Static Versus Dyanamic Typing, Part 1 *) 3 | 4 | datatype t = Int of int | String of string 5 | fun f y = if y > 0 then Int(y+y) else String "hi" 6 | 7 | fun foo x = case f x of 8 | Int i => Int.toString i 9 | | String s => s 10 | 11 | fun cube x = x * x * x 12 | 13 | val z = cube 7 14 | 15 | (* fun f g = (g 7, g true) *) (* does not type-check *) 16 | (* val pair_of_pairs = f (fn x => (x,x)) *) 17 | 18 | datatype tort = Int of int 19 | | String of string 20 | | Cons of tort * tort 21 | | Fun of tort -> tort 22 | (* would have more constructors *) 23 | 24 | val _ = if true 25 | then Fun (fn x => case x of Int i => Int (i*i*i)) 26 | else Cons (Int 7, String "hi") 27 | 28 | (* does not type-check *) 29 | (* 30 | fun pow x y = 31 | if y = 0 32 | then 1 33 | else x * pow (x,y-1) 34 | *) 35 | 36 | (* wrong algorithm *) 37 | fun pow x y = (* curried *) 38 | if y = 0 39 | then 1 40 | else x + pow x (y-1) (* oops *) 41 | -------------------------------------------------------------------------------- /resources/07code/122_eval.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 6: Optional: eval and quote 3 | 4 | #lang racket 5 | 6 | (define (make-some-code1 y) ; just returns a list 7 | (if y 8 | (list 'begin (list 'print "hi") (list '+ 4 2)) 9 | (list '+ 5 3))) 10 | 11 | (define (make-some-code2 y) ; same as make-some-code1 12 | (if y 13 | (quote (begin (print "hi") (+ 4 2))) 14 | (quote (+ 5 3)))) 15 | 16 | (define (test1) (eval (make-some-code1 #t))) ; prints "hi", result 6 17 | (define (test2) (eval (make-some-code1 #t))) ; prints "hi", result 6 18 | 19 | -------------------------------------------------------------------------------- /resources/07summary.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/resources/07summary.pdf -------------------------------------------------------------------------------- /resources/08code/123_ruby_intro.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: Introduction to Ruby 3 | 4 | class Hello 5 | 6 | def my_first_method 7 | puts "Hello, World!" 8 | end 9 | 10 | end 11 | 12 | x = Hello.new 13 | x.my_first_method 14 | -------------------------------------------------------------------------------- /resources/08code/124_classes_objects.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: Classes and Objects 3 | 4 | class A 5 | def m1 6 | 34 7 | end 8 | 9 | def m2 (x,y) 10 | z = 7 11 | if x > y 12 | false 13 | else 14 | x + y * z 15 | end 16 | end 17 | 18 | end 19 | 20 | class B 21 | def m1 22 | 4 23 | end 24 | 25 | def m3 x 26 | x.abs * 2 + self.m1 27 | end 28 | end 29 | 30 | # returning self is convenient for "stringing method calls" 31 | class C 32 | def m1 33 | print "hi " 34 | self 35 | end 36 | def m2 37 | print "bye " 38 | self 39 | end 40 | def m3 41 | print "\n" 42 | self 43 | end 44 | end 45 | 46 | # example uses (can type into irb) 47 | # here in a multiline comment, which is not well-known 48 | =begin 49 | a = A.new 50 | thirty_four = a.m1 51 | b = B.new 52 | four = b.m1 53 | forty_seven = B.new.m3 -17 54 | thirty_one = a.m2(3,four) 55 | 56 | c = C.new 57 | c.m1.m2.m3.m1.m1.m3 58 | =end 59 | -------------------------------------------------------------------------------- /resources/08code/125_object_state.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: Object State 3 | 4 | class A 5 | def m1 6 | @foo = 0 7 | end 8 | 9 | def m2 x 10 | @foo += x 11 | end 12 | 13 | def foo 14 | @foo 15 | end 16 | 17 | end 18 | 19 | class B 20 | # uses initialize method, which is better than m1 21 | # initialize can take arguments too (here providing defaults) 22 | def initialize(f=0) 23 | @foo = f 24 | end 25 | 26 | def m2 x 27 | @foo += x 28 | end 29 | 30 | def foo 31 | @foo 32 | end 33 | 34 | end 35 | 36 | class C 37 | # we now add in a class-variable, class-constant, and class-method 38 | 39 | Dans_Age = 38 40 | 41 | def self.reset_bar 42 | @@bar = 0 43 | end 44 | 45 | def initialize(f=0) 46 | @foo = f 47 | end 48 | 49 | def m2 x 50 | @foo += x 51 | @@bar += 1 52 | end 53 | 54 | def foo 55 | @foo 56 | end 57 | 58 | def bar 59 | @@bar 60 | end 61 | end 62 | 63 | # example uses 64 | =begin 65 | x = A.new 66 | y = A.new # different object than x 67 | z = x # alias to x 68 | x.foo # get back nil because instance variable not initialized 69 | x.m2 3 # error because try to add with nil object 70 | x.m1 # creates @foo in object x refers to 71 | z.foo # remember, x and z are aliases 72 | z.m2 3 73 | x.foo 74 | y.m1 75 | y.m2 4 76 | y.foo 77 | x.foo 78 | 79 | w = B.new 3 80 | v = B.new 81 | w.foo + v.foo 82 | 83 | d = C.new 17 84 | d.m2 5 85 | e = C.new 86 | e.m2 6 87 | d.bar 88 | forty = C::Dans_Age + d.bar 89 | 90 | =end 91 | -------------------------------------------------------------------------------- /resources/08code/127_example.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: A Longer Example 3 | 4 | class MyRational 5 | 6 | def initialize(num,den=1) # second argument has a default 7 | if den == 0 8 | raise "MyRational received an inappropriate argument" 9 | elsif den < 0 # notice non-english word elsif 10 | @num = - num # fields created when you assign to them 11 | @den = - den 12 | else 13 | @num = num # semicolons optional to separate expressions on different lines 14 | @den = den 15 | end 16 | reduce # i.e., self.reduce() but private so must write reduce or reduce() 17 | end 18 | 19 | def to_s 20 | ans = @num.to_s 21 | if @den != 1 # everything true except false _and_ nil objects 22 | ans += "/" 23 | ans += @den.to_s 24 | end 25 | ans 26 | end 27 | 28 | def to_s2 # using some unimportant syntax and a slightly different algorithm 29 | dens = "" 30 | dens = "/" + @den.to_s if @den != 1 31 | @num.to_s + dens 32 | end 33 | 34 | def to_s3 # using things like Racket's quasiquote and unquote 35 | "#{@num}#{if @den==1 then "" else "/" + @den.to_s end}" 36 | end 37 | 38 | def add! r # mutate self in-place 39 | a = r.num # only works b/c of protected methods below 40 | b = r.den # only works b/c of protected methods below 41 | c = @num 42 | d = @den 43 | @num = (a * d) + (b * c) 44 | @den = b * d 45 | reduce 46 | self # convenient for stringing calls 47 | end 48 | 49 | # a functional addition, so we can write r1.+ r2 to make a new rational 50 | # and built-in syntactic sugar will work: can write r1 + r2 51 | def + r 52 | ans = MyRational.new(@num,@den) 53 | ans.add! r 54 | ans 55 | end 56 | 57 | protected 58 | # there is very common sugar for this (attr_reader) 59 | # the better way: 60 | # attr_reader :num, :den 61 | # protected :num, :den 62 | # we do not want these methods public, but we cannot make them private 63 | # because of the add! method above 64 | def num 65 | @num 66 | end 67 | def den 68 | @den 69 | end 70 | 71 | private 72 | def gcd(x,y) # recursive method calls work as expected 73 | if x == y 74 | x 75 | elsif x < y 76 | gcd(x,y-x) 77 | else 78 | gcd(y,x) 79 | end 80 | end 81 | 82 | def reduce 83 | if @num == 0 84 | @den = 1 85 | else 86 | d = gcd(@num.abs, @den) # notice method call on number 87 | @num = @num / d 88 | @den = @den / d 89 | end 90 | end 91 | end 92 | 93 | # can have a top-level method (just part of Object class) for testing, etc. 94 | def use_rationals 95 | r1 = MyRational.new(3,4) 96 | r2 = r1 + r1 + MyRational.new(-5,2) 97 | puts r2.to_s 98 | (r2.add! r1).add! (MyRational.new(1,-4)) 99 | puts r2.to_s 100 | puts r2.to_s2 101 | puts r2.to_s3 102 | end 103 | -------------------------------------------------------------------------------- /resources/08code/129_classes_dynamic.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: Class Definitions are Dynamic 3 | 4 | require_relative './127_example' # similar to ML's use 5 | 6 | # above line defines the MyRational class, but now we can change it 7 | 8 | class MyRational 9 | def double 10 | self + self 11 | end 12 | end 13 | 14 | class Fixnum 15 | def double 16 | self + self 17 | end 18 | end 19 | 20 | def m 21 | 42 22 | end 23 | 24 | class Object 25 | def m 26 | 43 27 | end 28 | end 29 | 30 | # this one will crash irb 31 | # class Fixnum 32 | # def + x 33 | # 42 34 | # end 35 | # end 36 | -------------------------------------------------------------------------------- /resources/08code/131_arrays.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: Arrays Longer Example 3 | 4 | a = [3,2,7,9] 5 | a[2] 6 | a[0] 7 | a[4] 8 | a.size 9 | a[-1] 10 | a[-2] 11 | a[1] = 6 12 | a 13 | a[6] = 14 14 | a 15 | a[5] 16 | a.size 17 | 18 | a[3] = "hi" 19 | 20 | b = a + [true,false] 21 | c = [3,2,3] | [1,2,3] 22 | 23 | # array make fine tuples 24 | 25 | triple = [false, "hi", a[0] + 4] 26 | triple[2] 27 | 28 | # arrays can also have initial size chosen at run-time 29 | # (and as we saw can grow later -- and shrink) 30 | x = if a[1] < a[0] then 10 else 20 end 31 | y = Array.new(x) 32 | 33 | # better: initialized with a block (coming soon) 34 | z = Array.new(x) { 0 } 35 | w = Array.new(x) {|i| -i } 36 | 37 | # stacks 38 | a 39 | a.push 5 40 | a.push 7 41 | a.pop 42 | a.pop 43 | a.pop 44 | 45 | # queues (from either end) 46 | 47 | a.push 11 48 | a.shift 49 | a.shift 50 | a.unshift 14 51 | 52 | # aliasing 53 | 54 | d = a 55 | e = a + [] 56 | d[0] 57 | a[0] = 6 58 | d[0] 59 | e[0] 60 | 61 | # slices 62 | 63 | f = [2,4,6,8,10,12,14] 64 | f[2,4] 65 | f.slice(2,2) 66 | f.slice(-2,2) 67 | f[2,4] = [1,1] 68 | 69 | # iterating: next segment, teaser here: 70 | 71 | [1,3,4,12].each {|i| puts (i * i)} 72 | -------------------------------------------------------------------------------- /resources/08code/132_blocks.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: Passing Blocks 3 | 4 | 3.times { puts "hello" } 5 | 6 | [4,6,8].each { puts "hi" } 7 | 8 | i = 7 9 | [4,6,8].each {|x| if i > x then puts (x+1) end } 10 | 11 | a = Array.new(5) {|i| 4*(i+1)} 12 | a.each { puts "hi" } 13 | a.each {|x| puts (x * 2) } 14 | a.map {|x| x * 2 } #synonym: collect 15 | a.any? {|x| x > 7 } 16 | a.all? {|x| x > 7 } 17 | a.all? # implicit are elements "true" (i.e., neither false nor nil) 18 | a.inject(0) {|acc,elt| acc+elt } 19 | a.select {|x| x > 7 } #non-synonym: filter 20 | 21 | def t i 22 | (0..i).each do |j| 23 | print " " * j 24 | (j..i).each {|k| print k; print " "} 25 | print "\n" 26 | end 27 | end 28 | 29 | t 9 30 | 31 | -------------------------------------------------------------------------------- /resources/08code/133_using_blocks.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: Using Blocks 3 | 4 | class Foo 5 | def initialize(max) 6 | @max = max 7 | end 8 | 9 | def silly 10 | yield(4,5) + yield(@max,@max) 11 | end 12 | 13 | def count base 14 | if base > @max 15 | raise "reached max" 16 | elsif yield base 17 | 1 18 | else 19 | 1 + (count(base+1) {|i| yield i}) 20 | end 21 | end 22 | end 23 | 24 | f = Foo.new(1000) 25 | 26 | f.silly {|a,b| 2*a - b} 27 | 28 | f.count(10) {|i| (i * i) == (34 * i)} 29 | -------------------------------------------------------------------------------- /resources/08code/134_procs.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: Procs 3 | 4 | a = [3,5,7,9] 5 | 6 | # no need for Procs here 7 | b = a.map {|x| x + 1} 8 | 9 | i = b.count {|x| x >= 6} 10 | 11 | # need Procs here: want an array of functions 12 | c = a.map {|x| lambda {|y| x >= y} } 13 | 14 | # elements of c are Proc objects with a call method 15 | 16 | c[2].call 17 17 | 18 | j = c.count {|x| x.call(5) } 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /resources/08code/135_hashes_ranges.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: Hashes and Ranges 3 | 4 | 5 | h1 = {} 6 | h1["a"] = "Found A" 7 | h1[false] = "Found false" 8 | h1["a"] 9 | h1[false] 10 | h1[42] 11 | h1.keys 12 | h1.values 13 | h1.delete("a") 14 | 15 | h2 = {"SML"=>1, "Racket"=>2, "Ruby"=>3} 16 | h2["SML"] 17 | 18 | # Symbols are like strings, but cheaper. Often used with hashes. 19 | h3 = {:sml => 1, :racket => 2, :ruby => 3} 20 | 21 | # each for hashes best with 2-argument block 22 | 23 | h2.each {|k,v| print k; print ": "; puts v} 24 | 25 | # ranges 26 | (1..100).inject {|acc,elt| acc + elt} 27 | 28 | def foo a 29 | a.count {|x| x*x < 50} 30 | end 31 | 32 | # duck typing in foo 33 | foo [3,5,7,9] 34 | foo (3..9) 35 | -------------------------------------------------------------------------------- /resources/08code/136_subclassing.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: Subclassing 3 | 4 | class Point 5 | attr_accessor :x, :y 6 | 7 | def initialize(x,y) 8 | @x = x 9 | @y = y 10 | end 11 | def distFromOrigin 12 | Math.sqrt(@x * @x + @y * @y) # why a module method? Less OOP :-( 13 | end 14 | def distFromOrigin2 15 | Math.sqrt(x * x + y * y) # uses getter methods 16 | end 17 | 18 | end 19 | 20 | class ColorPoint < Point 21 | attr_accessor :color 22 | 23 | def initialize(x,y,c="clear") # or could skip this and color starts unset 24 | super(x,y) # keyword super calls same method in superclass 25 | @color = c 26 | end 27 | end 28 | 29 | # example uses with reflection 30 | p = Point.new(0,0) 31 | cp = ColorPoint.new(0,0,"red") 32 | p.class # Point 33 | p.class.superclass # Object 34 | cp.class # ColorPoint 35 | cp.class.superclass # Point 36 | cp.class.superclass.superclass # Object 37 | cp.is_a? Point # true 38 | cp.instance_of? Point # false 39 | cp.is_a? ColorPoint # true 40 | cp.instance_of? ColorPoint # true 41 | -------------------------------------------------------------------------------- /resources/08code/138_overriding.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 7: Overriding and Dynamic Dispatch 3 | 4 | class Point 5 | attr_accessor :x, :y 6 | 7 | def initialize(x,y) 8 | @x = x 9 | @y = y 10 | end 11 | def distFromOrigin 12 | Math.sqrt(@x * @x + @y * @y) # uses instance variables directly 13 | end 14 | def distFromOrigin2 15 | Math.sqrt(x * x + y * y) # uses getter methods 16 | end 17 | 18 | end 19 | 20 | # design question: "Is a 3D-point a 2D-point?" 21 | # [arguably poor style here, especially in statically typed OOP languages] 22 | class ThreeDPoint < Point 23 | attr_accessor :z 24 | 25 | def initialize(x,y,z) 26 | super(x,y) 27 | @z = z 28 | end 29 | def distFromOrigin 30 | d = super 31 | Math.sqrt(d * d + @z * @z) 32 | end 33 | def distFromOrigin2 34 | d = super 35 | Math.sqrt(d * d + z * z) 36 | end 37 | end 38 | 39 | class PolarPoint < Point 40 | # Interesting: by not calling super constructor, no x and y instance vars 41 | # In Java/C#/Smalltalk would just have unused x and y fields 42 | def initialize(r,theta) 43 | @r = r 44 | @theta = theta 45 | end 46 | def x 47 | @r * Math.cos(@theta) 48 | end 49 | def y 50 | @r * Math.sin(@theta) 51 | end 52 | def x= a 53 | b = y # avoids multiple calls to y method 54 | @theta = Math.atan2(b, a) 55 | @r = Math.sqrt(a*a + b*b) 56 | self 57 | end 58 | def y= b 59 | a = x # avoid multiple calls to y method 60 | @theta = Math.atan2(b, a) 61 | @r = Math.sqrt(a*a + b*b) 62 | self 63 | end 64 | def distFromOrigin # must override since inherited method does wrong thing 65 | @r 66 | end 67 | # inherited distFromOrigin2 already works!! 68 | end 69 | 70 | # the key example 71 | pp = PolarPoint.new(4,Math::PI/4) 72 | pp.x 73 | pp.y 74 | pp.distFromOrigin2 75 | -------------------------------------------------------------------------------- /resources/08code/140_dynamic_dispatch_vs_closures.rb: -------------------------------------------------------------------------------- 1 | # Dan Grossman, Programming Languages 2 | # Section 7: Dynamic Dispatch Versus Closures 3 | 4 | # Ruby methods use late binding: simple example: 5 | 6 | class A 7 | def even x 8 | puts "in even A" 9 | if x==0 then true else odd(x-1) end 10 | end 11 | def odd x 12 | puts "in odd A" 13 | if x==0 then false else even(x-1) end 14 | end 15 | end 16 | 17 | a1 = A.new.odd 7 18 | puts "a1 is " + a1.to_s + "\n\n" 19 | 20 | class B < A 21 | def even x # changes B's odd too! (helps) 22 | puts "in even B" 23 | x % 2 == 0 24 | end 25 | end 26 | 27 | a2 = B.new.odd 7 28 | puts "a2 is " + a2.to_s + "\n\n" 29 | 30 | class C < A 31 | def even x 32 | puts "in even C" # changes C's odd too! (hurts) 33 | false 34 | end 35 | end 36 | 37 | a3 = C.new.odd 7 38 | puts "a3 is " + a3.to_s + "\n\n" 39 | 40 | -------------------------------------------------------------------------------- /resources/08code/140_dynamic_dispatch_vs_closures.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 7: Dynamic Dispatch Versus Closures *) 3 | 4 | (* ML functions do not use use late binding: simple example: *) 5 | 6 | fun even x = (print "in even\n" ; if x=0 then true else odd (x-1)) 7 | and odd x = (print "in odd\n" ; if x=0 then false else even (x-1)) 8 | 9 | val a1 = odd 7 10 | val _ = print "\n" 11 | 12 | (* does not change behavior of odd -- which is too bad *) 13 | fun even x = (x mod 2) = 0 14 | 15 | val a2 = odd 7 16 | val _ = print "\n" 17 | 18 | (* does not change behavior of odd -- which is good *) 19 | fun even x = false 20 | 21 | val a3 = odd 7 22 | val _ = print "\n" 23 | 24 | -------------------------------------------------------------------------------- /resources/08code/141_manual_dynamic_dispatch.rkt: -------------------------------------------------------------------------------- 1 | ; Programming Languages, Dan Grossman 2 | ; Section 7: Optional: Dynamic Dispatch Manually in Racket 3 | 4 | #lang racket 5 | 6 | ;; We can "use" dynamic dispatch in a language without it manually 7 | 8 | ;; Our "objects" will have: 9 | ;; * an immutable list of mutable "fields" (symbols and contents) 10 | ;; * an immutable list of immutable "methods" (symbols and functions taking self) 11 | (struct obj (fields methods)) 12 | 13 | ; like assoc but for an immutable list of mutable pairs 14 | (define (assoc-m v xs) 15 | (cond [(null? xs) #f] 16 | [(equal? v (mcar (car xs))) (car xs)] 17 | [#t (assoc-m v (cdr xs))])) 18 | 19 | (define (get obj fld) 20 | (let ([pr (assoc-m fld (obj-fields obj))]) 21 | (if pr 22 | (mcdr pr) 23 | (error "field not found")))) 24 | 25 | (define (set obj fld v) 26 | (let ([pr (assoc-m fld (obj-fields obj))]) 27 | (if pr 28 | (set-mcdr! pr v) 29 | (error "field not found")))) 30 | 31 | (define (send obj msg . args) ; convenience: multi-argument functions (2+ arguments) 32 | (let ([pr (assoc msg (obj-methods obj))]) 33 | (if pr 34 | ((cdr pr) obj args) ; do the call 35 | (error "method not found" msg)))) 36 | 37 | (define (make-point _x _y) 38 | (obj 39 | (list (mcons 'x _x) 40 | (mcons 'y _y)) 41 | (list (cons 'get-x (lambda (self args) (get self 'x))) 42 | (cons 'get-y (lambda (self args) (get self 'y))) 43 | (cons 'set-x (lambda (self args) (set self 'x (car args)))) 44 | (cons 'set-y (lambda (self args) (set self 'y (car args)))) 45 | (cons 'distToOrigin 46 | (lambda (self args) 47 | (let ([a (send self 'get-x)] 48 | [b (send self 'get-y)]) 49 | (sqrt (+ (* a a) (* b b))))))))) 50 | 51 | (define (make-color-point _x _y _c) 52 | (let ([pt (make-point _x _y)]) 53 | (obj 54 | (cons (mcons 'color _c) 55 | (obj-fields pt)) 56 | (append (list 57 | (cons 'get-color (lambda (self args) (get self 'color))) 58 | (cons 'set-color (lambda (self args) (set self 'color (car args))))) 59 | (obj-methods pt))))) 60 | 61 | (define (make-polar-point _r _th) 62 | (let ([pt (make-point #f #f)]) 63 | (obj 64 | (append (list (mcons 'r _r) 65 | (mcons 'theta _th)) 66 | (obj-fields pt)) ; Java-style field extension 67 | (append ; overriding by being earlier in the list (see send function) 68 | (list 69 | (cons 'set-r-theta 70 | (lambda (self args) 71 | (begin 72 | (set self 'r (car args)) 73 | (set self 'theta (cadr args))))) 74 | (cons 'get-x (lambda (self args) 75 | (let ([r (get self 'r)] 76 | [theta (get self 'theta)]) 77 | (* r (cos theta))))) 78 | (cons 'get-y (lambda (self args) 79 | (let ([r (get self 'r)] 80 | [theta (get self 'theta)]) 81 | (* r (sin theta))))) 82 | (cons 'set-x (lambda (self args) 83 | (let* ([a (car args)] 84 | [b (send self 'get-y)] 85 | [theta (atan b a)] 86 | [r (sqrt (+ (* a a) (* b b)))]) 87 | (send self 'set-r-theta r theta)))) 88 | (cons 'set-y (lambda (self args) 89 | (let* ([b (car args)] 90 | [a (send self 'get-x)] 91 | [theta (atan b a)] 92 | [r (sqrt (+ (* a a) (* b b)))]) 93 | (send self 'set-r-theta r theta))))) 94 | (obj-methods pt))))) 95 | 96 | (define p1 (make-point -4 0)) 97 | p1 98 | (send p1 'get-x) 99 | (send p1 'get-y) 100 | (send p1 'distToOrigin) 101 | (send p1 'set-y 3) 102 | (send p1 'distToOrigin) 103 | 104 | (define p2 (make-color-point -4 0 "red")) 105 | p2 106 | (send p2 'get-x) 107 | (send p2 'get-y) 108 | (send p2 'distToOrigin) 109 | (send p2 'set-y 3) 110 | (send p2 'distToOrigin) 111 | 112 | (define p3 (make-polar-point 4 3.1415926535)) 113 | p3 114 | (send p3 'get-x) 115 | (send p3 'get-y) 116 | (send p3 'distToOrigin) 117 | (send p3 'set-y 3) 118 | (send p3 'distToOrigin) 119 | -------------------------------------------------------------------------------- /resources/08summary.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/resources/08summary.pdf -------------------------------------------------------------------------------- /resources/09code/142_fp_oo_decomposition.java: -------------------------------------------------------------------------------- 1 | // Programming Languages, Dan Grossman 2 | // Section 8: OOP vs. Functional Decomposition 3 | 4 | abstract class Exp { 5 | abstract Value eval(); // no argument because no environment 6 | abstract String toStrng(); // renaming b/c toString in Object is public 7 | abstract boolean hasZero(); 8 | } 9 | 10 | // this level of the class hierarchy is overkill here but will provide 11 | // a more direct contrast with more complicated examples soon 12 | abstract class Value extends Exp { 13 | 14 | } 15 | 16 | class Int extends Value { 17 | public int i; 18 | Int(int i) { 19 | this.i = i; 20 | } 21 | Value eval() { 22 | return this; 23 | } 24 | String toStrng() { 25 | return "" + i; 26 | } 27 | boolean hasZero() { 28 | return i==0; 29 | } 30 | } 31 | 32 | class Negate extends Exp { 33 | public Exp e; 34 | Negate(Exp e) { 35 | this.e = e; 36 | } 37 | Value eval() { 38 | // we downcast from Exp to Int, which will raise a run-time error 39 | // if the subexpression does not evaluate to an Int 40 | return new Int(- ((Int)(e.eval())).i); 41 | } 42 | String toStrng() { 43 | return "-(" + e.toStrng() + ")"; 44 | } 45 | boolean hasZero() { 46 | return e.hasZero(); 47 | } 48 | } 49 | 50 | class Add extends Exp { 51 | Exp e1; 52 | Exp e2; 53 | Add(Exp e1, Exp e2) { 54 | this.e1 = e1; 55 | this.e2 = e2; 56 | } 57 | Value eval() { 58 | // we downcast from Exp to Int, which will raise a run-time error 59 | // if either subexpression does not evaluate to an Int 60 | return new Int(((Int)(e1.eval())).i + ((Int)(e2.eval())).i); 61 | } 62 | String toStrng() { 63 | return "(" + e1.toStrng() + " + " + e2.toStrng() + ")"; 64 | } 65 | boolean hasZero() { 66 | return e1.hasZero() || e2.hasZero(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /resources/09code/142_fp_oo_decomposition.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 8: OOP vs. Functional Decomposition 3 | 4 | # Note: If Exp and Value are empty classes, we do not need them in a 5 | # dynamically typed language, but they help show the structure and they 6 | # can be useful places for code that applies to multiple subclasses. 7 | 8 | class Exp 9 | # could put default implementations or helper methods here 10 | end 11 | 12 | class Value < Exp 13 | # this is overkill here, but is useful if you have multiple kinds of 14 | # /values/ in your language that can share methods that do not make sense 15 | # for non-value expressions 16 | end 17 | 18 | class Int < Value 19 | attr_reader :i 20 | def initialize i 21 | @i = i 22 | end 23 | def eval # no argument because no environment 24 | self 25 | end 26 | def toString 27 | @i.to_s 28 | end 29 | def hasZero 30 | i==0 31 | end 32 | end 33 | 34 | class Negate < Exp 35 | attr_reader :e 36 | def initialize e 37 | @e = e 38 | end 39 | def eval 40 | Int.new(-e.eval.i) # error if e.eval has no i method 41 | end 42 | def toString 43 | "-(" + e.toString + ")" 44 | end 45 | def hasZero 46 | e.hasZero 47 | end 48 | end 49 | 50 | class Add < Exp 51 | attr_reader :e1, :e2 52 | def initialize(e1,e2) 53 | @e1 = e1 54 | @e2 = e2 55 | end 56 | def eval 57 | Int.new(e1.eval.i + e2.eval.i) # error if e1.eval or e2.eval has no i method 58 | end 59 | def toString 60 | "(" + e1.toString + " + " + e2.toString + ")" 61 | end 62 | def hasZero 63 | e1.hasZero || e2.hasZero 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /resources/09code/142_fp_oo_decomposition.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 8: OOP vs. Functional Decomposition *) 3 | 4 | datatype exp = 5 | Int of int 6 | | Negate of exp 7 | | Add of exp * exp 8 | 9 | exception BadResult of string 10 | 11 | (* this helper function is overkill here but will provide a more 12 | direct contrast with more complicated examples soon *) 13 | fun add_values (v1,v2) = 14 | case (v1,v2) of 15 | (Int i, Int j) => Int (i+j) 16 | | _ => raise BadResult "non-values passed to add_values" 17 | 18 | fun eval e = (* no environment because we don't have variables *) 19 | case e of 20 | Int _ => e 21 | | Negate e1 => (case eval e1 of 22 | Int i => Int (~i) 23 | | _ => raise BadResult "non-int in negation") 24 | | Add(e1,e2) => add_values (eval e1, eval e2) 25 | 26 | fun toString e = 27 | case e of 28 | Int i => Int.toString i 29 | | Negate e1 => "-(" ^ (toString e1) ^ ")" 30 | | Add(e1,e2) => "(" ^ (toString e1) ^ " + " ^ (toString e2) ^ ")" 31 | 32 | fun hasZero e = 33 | case e of 34 | Int i => i=0 35 | | Negate e1 => hasZero e1 36 | | Add(e1,e2) => (hasZero e1) orelse (hasZero e2) 37 | 38 | -------------------------------------------------------------------------------- /resources/09code/143_adding_operations_or_variants.java: -------------------------------------------------------------------------------- 1 | // Programming Languages, Dan Grossman 2 | // Section 8: Adding Operations or Variants 3 | 4 | // we take our previous version and add noNegConstants which requires 5 | // changing all previous code, with a nice to-do list due to non-abstract 6 | // classes, and a Mult constructor without changing old code. 7 | 8 | abstract class Exp { 9 | abstract Value eval(); // no argument because no environment 10 | abstract String toStrng(); // renaming b/c toString in Object is public 11 | abstract boolean hasZero(); 12 | abstract Exp noNegConstants(); 13 | } 14 | 15 | // this level of the class hierarchy is overkill here but will provide 16 | // a more direct contrast with more complicated examples soon 17 | abstract class Value extends Exp { 18 | 19 | } 20 | 21 | class Int extends Value { 22 | public int i; 23 | Int(int i) { 24 | this.i = i; 25 | } 26 | Value eval() { 27 | return this; 28 | } 29 | String toStrng() { 30 | return "" + i; 31 | } 32 | boolean hasZero() { 33 | return i==0; 34 | } 35 | Exp noNegConstants() { 36 | if(i < 0) 37 | return new Negate(new Int(-i)); 38 | else 39 | return this; 40 | } 41 | } 42 | 43 | class Negate extends Exp { 44 | public Exp e; 45 | Negate(Exp e) { 46 | this.e = e; 47 | } 48 | Value eval() { 49 | // we downcast from Exp to Int, which will raise a run-time error 50 | // if the subexpression does not evaluate to an Int 51 | return new Int(- ((Int)(e.eval())).i); 52 | } 53 | String toStrng() { 54 | return "-(" + e.toStrng() + ")"; 55 | } 56 | boolean hasZero() { 57 | return e.hasZero(); 58 | } 59 | Exp noNegConstants() { 60 | return new Negate(e.noNegConstants()); 61 | } 62 | } 63 | 64 | class Add extends Exp { 65 | Exp e1; 66 | Exp e2; 67 | Add(Exp e1, Exp e2) { 68 | this.e1 = e1; 69 | this.e2 = e2; 70 | } 71 | Value eval() { 72 | // we downcast from Exp to Int, which will raise a run-time error 73 | // if either subexpression does not evaluate to an Int 74 | return new Int(((Int)(e1.eval())).i + ((Int)(e2.eval())).i); 75 | } 76 | String toStrng() { 77 | return "(" + e1.toStrng() + " + " + e2.toStrng() + ")"; 78 | } 79 | boolean hasZero() { 80 | return e1.hasZero() || e2.hasZero(); 81 | } 82 | Exp noNegConstants() { 83 | return new Add(e1.noNegConstants(), e2.noNegConstants()); 84 | } 85 | } 86 | 87 | class Mult extends Exp { 88 | Exp e1; 89 | Exp e2; 90 | Mult(Exp e1, Exp e2) { 91 | this.e1 = e1; 92 | this.e2 = e2; 93 | } 94 | Value eval() { 95 | // we downcast from Exp to Int, which will raise a run-time error 96 | // if either subexpression does not evaluate to an Int 97 | return new Int(((Int)(e1.eval())).i * ((Int)(e2.eval())).i); 98 | } 99 | String toStrng() { 100 | return "(" + e1.toStrng() + " * " + e2.toStrng() + ")"; 101 | } 102 | boolean hasZero() { 103 | return e1.hasZero() || e2.hasZero(); 104 | } 105 | 106 | Exp noNegConstants() { 107 | return new Mult(e1.noNegConstants(), e2.noNegConstants()); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /resources/09code/143_adding_operations_or_variants.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 8: Adding Operations or Variants 3 | 4 | # Note: If Exp and Value are empty classes, we do not need them in a 5 | # dynamically typed language, but they help show the structure and they 6 | # can be useful places for code that applies to multiple subclasses. 7 | 8 | class Exp 9 | # could put default implementations or helper methods here 10 | end 11 | 12 | class Value < Exp 13 | # this is overkill here, but is useful if you have multiple kinds of 14 | # /values/ in your language that can share methods that do not make sense 15 | # for non-value expressions 16 | end 17 | 18 | class Int < Value 19 | attr_reader :i 20 | def initialize i 21 | @i = i 22 | end 23 | def eval # no argument because no environment 24 | self 25 | end 26 | def toString 27 | @i.to_s 28 | end 29 | def hasZero 30 | i==0 31 | end 32 | def noNegConstants 33 | if i < 0 34 | Negate.new(Int.new(-i)) 35 | else 36 | self 37 | end 38 | end 39 | end 40 | 41 | class Negate < Exp 42 | attr_reader :e 43 | def initialize e 44 | @e = e 45 | end 46 | def eval 47 | Int.new(-e.eval.i) # error if e.eval has no i method 48 | end 49 | def toString 50 | "-(" + e.toString + ")" 51 | end 52 | def hasZero 53 | e.hasZero 54 | end 55 | def noNegConstants 56 | Negate.new(e.noNegConstants) 57 | end 58 | end 59 | 60 | class Add < Exp 61 | attr_reader :e1, :e2 62 | def initialize(e1,e2) 63 | @e1 = e1 64 | @e2 = e2 65 | end 66 | def eval 67 | Int.new(e1.eval.i + e2.eval.i) # error if e1.eval or e2.eval has no i method 68 | end 69 | def toString 70 | "(" + e1.toString + " + " + e2.toString + ")" 71 | end 72 | def hasZero 73 | e1.hasZero || e2.hasZero 74 | end 75 | def noNegConstants 76 | Add.new(e1.noNegConstants,e2.noNegConstants) 77 | end 78 | end 79 | 80 | class Mult < Exp 81 | attr_reader :e1, :e2 82 | def initialize(e1,e2) 83 | @e1 = e1 84 | @e2 = e2 85 | end 86 | def eval 87 | Int.new(e1.eval.i * e2.eval.i) # error if e1.eval or e2.eval has no i method 88 | end 89 | def toString 90 | "(" + e1.toString + " * " + e2.toString + ")" 91 | end 92 | def hasZero 93 | e1.hasZero || e2.hasZero 94 | end 95 | def noNegConstants 96 | Mult.new(e1.noNegConstants,e2.noNegConstants) 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /resources/09code/143_adding_operations_or_variants.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 8: Adding Operations or Variants *) 3 | 4 | (* we take our previous version and add noNegConstants without changing 5 | old code and a Mult constructor, which requires changing all previous code, 6 | with a nice to-do list due to inexhaustive pattern-matches. *) 7 | 8 | datatype exp = 9 | Int of int 10 | | Negate of exp 11 | | Add of exp * exp 12 | | Mult of exp * exp 13 | 14 | exception BadResult of string 15 | 16 | (* this helper function is overkill here but will provide a more 17 | direct contrast with more complicated examples soon *) 18 | fun add_values (v1,v2) = 19 | case (v1,v2) of 20 | (Int i, Int j) => Int (i+j) 21 | | _ => raise BadResult "non-values passed to add_values" 22 | 23 | fun eval e = (* no environment because we don't have variables *) 24 | case e of 25 | Int _ => e 26 | | Negate e1 => (case eval e1 of 27 | Int i => Int (~i) 28 | | _ => raise BadResult "non-int in negation") 29 | | Add(e1,e2) => add_values (eval e1, eval e2) 30 | | Mult(e1,e2) => (case (eval e1, eval e2) of 31 | (Int i, Int j) => Int (i*j) 32 | | _ => raise BadResult "non-ints in multiply") 33 | 34 | fun toString e = 35 | case e of 36 | Int i => Int.toString i 37 | | Negate e1 => "-(" ^ (toString e1) ^ ")" 38 | | Add(e1,e2) => "(" ^ (toString e1) ^ " + " ^ (toString e2) ^ ")" 39 | | Mult(e1,e2) => "(" ^ (toString e1) ^ " * " ^ (toString e2) ^ ")" 40 | 41 | fun hasZero e = 42 | case e of 43 | Int i => i=0 44 | | Negate e1 => hasZero e1 45 | | Add(e1,e2) => (hasZero e1) orelse (hasZero e2) 46 | | Mult(e1,e2) => (hasZero e1) orelse (hasZero e2) 47 | 48 | fun noNegConstants e = 49 | case e of 50 | Int i => if i < 0 then Negate (Int(~i)) else e 51 | | Negate e1 => Negate(noNegConstants e1) 52 | | Add(e1,e2) => Add(noNegConstants e1, noNegConstants e2) 53 | | Mult(e1,e2) => Mult(noNegConstants e1, noNegConstants e2) 54 | -------------------------------------------------------------------------------- /resources/09code/144_binary_methods_fp.sml: -------------------------------------------------------------------------------- 1 | (* Programming Languages, Dan Grossman *) 2 | (* Section 8: Binary Methods With Functional Decomposition *) 3 | 4 | datatype exp = 5 | Int of int 6 | | Negate of exp 7 | | Add of exp * exp 8 | | Mult of exp * exp 9 | | String of string 10 | | Rational of int * int 11 | 12 | exception BadResult of string 13 | 14 | fun add_values (v1,v2) = 15 | case (v1,v2) of 16 | (Int i, Int j) => Int (i+j) 17 | | (Int i, String s) => String(Int.toString i ^ s) 18 | | (Int i, Rational(j,k)) => Rational(i*k+j,k) 19 | | (String s, Int i) => String(s ^ Int.toString i) (* not commutative *) 20 | | (String s1, String s2) => String(s1 ^ s2) 21 | | (String s, Rational(i,j)) => String(s ^ Int.toString i ^ "/" ^ Int.toString j) 22 | | (Rational _, Int _) => add_values(v2,v1) (* commutative: avoid duplication *) 23 | | (Rational(i,j), String s) => String(Int.toString i ^ "/" ^ Int.toString j ^ s) 24 | | (Rational(a,b), Rational(c,d)) => Rational(a*d+b*c,b*d) 25 | | _ => raise BadResult "non-values passed to add_values" 26 | 27 | fun eval e = 28 | case e of 29 | Int _ => e 30 | | Negate e1 => (case eval e1 of 31 | Int i => Int (~i) 32 | | _ => raise BadResult "non-int in negation") 33 | | Add(e1,e2) => add_values (eval e1, eval e2) 34 | | Mult(e1,e2) => (case (eval e1, eval e2) of 35 | (Int i, Int j) => Int (i*j) 36 | | _ => raise BadResult "non-ints in multiply") 37 | | String _ => e 38 | | Rational _ => e 39 | 40 | fun toString e = 41 | case e of 42 | Int i => Int.toString i 43 | | Negate e1 => "-(" ^ (toString e1) ^ ")" 44 | | Add(e1,e2) => "(" ^ (toString e1) ^ " + " ^ (toString e2) ^ ")" 45 | | Mult(e1,e2) => "(" ^ (toString e1) ^ " * " ^ (toString e2) ^ ")" 46 | | String s => s 47 | | Rational(i,j) => Int.toString i ^ "/" ^ Int.toString j 48 | 49 | fun hasZero e = 50 | case e of 51 | Int i => i=0 52 | | Negate e1 => hasZero e1 53 | | Add(e1,e2) => (hasZero e1) orelse (hasZero e2) 54 | | Mult(e1,e2) => (hasZero e1) orelse (hasZero e2) 55 | | String _ => false 56 | | Rational(i,j) => i=0 57 | 58 | fun noNegConstants e = 59 | case e of 60 | Int i => if i < 0 then Negate (Int(~i)) else e 61 | | Negate e1 => Negate(noNegConstants e1) 62 | | Add(e1,e2) => Add(noNegConstants e1, noNegConstants e2) 63 | | Mult(e1,e2) => Mult(noNegConstants e1, noNegConstants e2) 64 | | String _ => e 65 | | Rational(i,j) => if i < 0 andalso j < 0 66 | then Rational(~i,~j) 67 | else if j < 0 68 | then Negate(Rational(i,~j)) 69 | else if i < 0 70 | then Negate(Rational(~i,j)) 71 | else e 72 | -------------------------------------------------------------------------------- /resources/09code/145_double_dispatch.java: -------------------------------------------------------------------------------- 1 | // Programming Languages, Dan Grossman 2 | // Section 8: Binary Methods with OOP: Double Dispatch 3 | 4 | abstract class Exp { 5 | abstract Value eval(); // no argument because no environment 6 | abstract String toStrng(); // renaming b/c toString in Object is public 7 | abstract boolean hasZero(); 8 | abstract Exp noNegConstants(); 9 | } 10 | 11 | abstract class Value extends Exp { 12 | abstract Value add_values(Value other); // first dispatch 13 | abstract Value addInt(Int other); // second dispatch 14 | abstract Value addString(MyString other); // second dispatch 15 | abstract Value addRational(Rational other); // second dispatch 16 | } 17 | 18 | class Int extends Value { 19 | public int i; 20 | Int(int i) { 21 | this.i = i; 22 | } 23 | Value eval() { 24 | return this; 25 | } 26 | String toStrng() { 27 | return "" + i; 28 | } 29 | boolean hasZero() { 30 | return i==0; 31 | } 32 | Exp noNegConstants() { 33 | if(i < 0) 34 | return new Negate(new Int(-i)); 35 | else 36 | return this; 37 | } 38 | Value add_values(Value other) { 39 | return other.addInt(this); 40 | } 41 | Value addInt(Int other) { 42 | return new Int(other.i + i); 43 | } 44 | Value addString(MyString other) { 45 | return new MyString(other.s + i); 46 | } 47 | Value addRational(Rational other) { 48 | return new Rational(other.i+other.j*i,other.j); 49 | } 50 | } 51 | 52 | class MyString extends Value { 53 | public String s; 54 | MyString(String s) { 55 | this.s = s; 56 | } 57 | Value eval() { 58 | return this; 59 | } 60 | String toStrng() { 61 | return s; 62 | } 63 | boolean hasZero() { 64 | return false; 65 | } 66 | 67 | Exp noNegConstants() { 68 | return this; 69 | } 70 | 71 | Value add_values(Value other) { 72 | return other.addString(this); 73 | } 74 | Value addInt(Int other) { 75 | return new MyString("" + other.i + s); 76 | } 77 | Value addString(MyString other) { 78 | return new MyString(other.s + s); 79 | } 80 | Value addRational(Rational other) { 81 | return new MyString("" + other.i + "/" + other.j + s); 82 | } 83 | } 84 | 85 | class Rational extends Value { 86 | int i; 87 | int j; 88 | Rational(int i, int j) { 89 | this.i = i; 90 | this.j = j; 91 | } 92 | Value eval() { 93 | return this; 94 | } 95 | String toStrng() { 96 | return "" + i + "/" + j; 97 | } 98 | boolean hasZero() { 99 | return i==0; 100 | } 101 | Exp noNegConstants() { 102 | if(i < 0 && j < 0) 103 | return new Rational(-i,-j); 104 | else if(j < 0) 105 | return new Negate(new Rational(i,-j)); 106 | else if(i < 0) 107 | return new Negate(new Rational(-i,j)); 108 | else 109 | return this; 110 | } 111 | Value add_values(Value other) { 112 | return other.addRational(this); 113 | } 114 | Value addInt(Int other) { 115 | return other.addRational(this); // reuse computation of commutative operation 116 | 117 | } 118 | Value addString(MyString other) { 119 | return new MyString(other.s + i + "/" + j); 120 | } 121 | Value addRational(Rational other) { 122 | int a = i; 123 | int b = j; 124 | int c = other.i; 125 | int d = other.j; 126 | return new Rational(a*d+b*c,b*d); 127 | } 128 | } 129 | 130 | class Negate extends Exp { 131 | public Exp e; 132 | Negate(Exp e) { 133 | this.e = e; 134 | } 135 | Value eval() { 136 | // we downcast from Exp to Int, which will raise a run-time error 137 | // if the subexpression does not evaluate to an Int 138 | return new Int(- ((Int)(e.eval())).i); 139 | } 140 | String toStrng() { 141 | return "-(" + e.toStrng() + ")"; 142 | } 143 | boolean hasZero() { 144 | return e.hasZero(); 145 | } 146 | Exp noNegConstants() { 147 | return new Negate(e.noNegConstants()); 148 | } 149 | } 150 | 151 | class Add extends Exp { 152 | Exp e1; 153 | Exp e2; 154 | Add(Exp e1, Exp e2) { 155 | this.e1 = e1; 156 | this.e2 = e2; 157 | } 158 | Value eval() { 159 | return e1.eval().add_values(e2.eval()); 160 | } 161 | String toStrng() { 162 | return "(" + e1.toStrng() + " + " + e2.toStrng() + ")"; 163 | } 164 | boolean hasZero() { 165 | return e1.hasZero() || e2.hasZero(); 166 | } 167 | Exp noNegConstants() { 168 | return new Add(e1.noNegConstants(), e2.noNegConstants()); 169 | } 170 | } 171 | 172 | class Mult extends Exp { 173 | Exp e1; 174 | Exp e2; 175 | Mult(Exp e1, Exp e2) { 176 | this.e1 = e1; 177 | this.e2 = e2; 178 | } 179 | Value eval() { 180 | // we downcast from Exp to Int, which will raise a run-time error 181 | // if either subexpression does not evaluate to an Int 182 | return new Int(((Int)(e1.eval())).i * ((Int)(e2.eval())).i); 183 | } 184 | String toStrng() { 185 | return "(" + e1.toStrng() + " * " + e2.toStrng() + ")"; 186 | } 187 | boolean hasZero() { 188 | return e1.hasZero() || e2.hasZero(); 189 | } 190 | 191 | Exp noNegConstants() { 192 | return new Mult(e1.noNegConstants(), e2.noNegConstants()); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /resources/09code/145_double_dispatch.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 8: Binary Methods with OOP: Double Dispatch 3 | 4 | # Note: If Exp and Value are empty classes, we do not need them in a 5 | # dynamically typed language, but they help show the structure and they 6 | # can be useful places for code that applies to multiple subclasses. 7 | 8 | class Exp 9 | # could put default implementations or helper methods here 10 | end 11 | 12 | class Value < Exp 13 | # this is overkill here, but is useful if you have multiple kinds of 14 | # /values/ in your language that can share methods that do not make sense 15 | # for non-value expressions 16 | end 17 | 18 | class Int < Value 19 | attr_reader :i 20 | def initialize i 21 | @i = i 22 | end 23 | def eval # no argument because no environment 24 | self 25 | end 26 | def toString 27 | @i.to_s 28 | end 29 | def hasZero 30 | i==0 31 | end 32 | def noNegConstants 33 | if i < 0 34 | Negate.new(Int.new(-i)) 35 | else 36 | self 37 | end 38 | end 39 | # double-dispatch for adding values 40 | def add_values v # first dispatch 41 | v.addInt self 42 | end 43 | def addInt v # second dispatch: other is Int 44 | Int.new(v.i + i) 45 | end 46 | def addString v # second dispatch: other is MyString (notice order flipped) 47 | MyString.new(v.s + i.to_s) 48 | end 49 | def addRational v # second dispatch: other is MyRational 50 | MyRational.new(v.i+v.j*i,v.j) 51 | end 52 | end 53 | 54 | # new value classes -- avoiding name-conflict with built-in String, Rational 55 | class MyString < Value 56 | attr_reader :s 57 | def initialize s 58 | @s = s 59 | end 60 | def eval 61 | self 62 | end 63 | def toString 64 | s 65 | end 66 | def hasZero 67 | false 68 | end 69 | def noNegConstants 70 | self 71 | end 72 | 73 | # double-dispatch for adding values 74 | def add_values v # first dispatch 75 | v.addString self 76 | end 77 | def addInt v # second dispatch: other is Int (notice order is flipped) 78 | MyString.new(v.i.to_s + s) 79 | end 80 | def addString v # second dispatch: other is MyString (notice order flipped) 81 | MyString.new(v.s + s) 82 | end 83 | def addRational v # second dispatch: other is MyRational (notice order flipped) 84 | MyString.new(v.i.to_s + "/" + v.j.to_s + s) 85 | end 86 | end 87 | 88 | class MyRational < Value 89 | attr_reader :i, :j 90 | def initialize(i,j) 91 | @i = i 92 | @j = j 93 | end 94 | def eval 95 | self 96 | end 97 | def toString 98 | i.to_s + "/" + j.to_s 99 | end 100 | def hasZero 101 | i==0 102 | end 103 | def noNegConstants 104 | if i < 0 && j < 0 105 | MyRational.new(-i,-j) 106 | elsif j < 0 107 | Negate.new(MyRational.new(i,-j)) 108 | elsif i < 0 109 | Negate.new(MyRational.new(-i,j)) 110 | else 111 | self 112 | end 113 | end 114 | 115 | # double-dispatch for adding values 116 | def add_values v # first dispatch 117 | v.addRational self 118 | end 119 | def addInt v # second dispatch 120 | v.addRational self # reuse computation of commutative operation 121 | end 122 | def addString v # second dispatch: other is MyString (notice order flipped) 123 | MyString.new(v.s + i.to_s + "/" + j.to_s) 124 | end 125 | def addRational v # second dispatch: other is MyRational (notice order flipped) 126 | a,b,c,d = i,j,v.i,v.j 127 | MyRational.new(a*d+b*c,b*d) 128 | end 129 | end 130 | 131 | class Negate < Exp 132 | attr_reader :e 133 | def initialize e 134 | @e = e 135 | end 136 | def eval 137 | Int.new(-e.eval.i) # error if e.eval has no i method 138 | end 139 | def toString 140 | "-(" + e.toString + ")" 141 | end 142 | def hasZero 143 | e.hasZero 144 | end 145 | def noNegConstants 146 | Negate.new(e.noNegConstants) 147 | end 148 | end 149 | 150 | class Add < Exp 151 | attr_reader :e1, :e2 152 | def initialize(e1,e2) 153 | @e1 = e1 154 | @e2 = e2 155 | end 156 | def eval 157 | e1.eval.add_values e2.eval 158 | end 159 | def toString 160 | "(" + e1.toString + " + " + e2.toString + ")" 161 | end 162 | def hasZero 163 | e1.hasZero || e2.hasZero 164 | end 165 | def noNegConstants 166 | Add.new(e1.noNegConstants,e2.noNegConstants) 167 | end 168 | end 169 | 170 | class Mult < Exp 171 | attr_reader :e1, :e2 172 | def initialize(e1,e2) 173 | @e1 = e1 174 | @e2 = e2 175 | end 176 | def eval 177 | Int.new(e1.eval.i * e2.eval.i) # error if e1.eval or e2.eval has no i method 178 | end 179 | def toString 180 | "(" + e1.toString + " * " + e2.toString + ")" 181 | end 182 | def hasZero 183 | e1.hasZero || e2.hasZero 184 | end 185 | def noNegConstants 186 | Mult.new(e1.noNegConstants,e2.noNegConstants) 187 | end 188 | end 189 | -------------------------------------------------------------------------------- /resources/09code/147_multiple_inheritance.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 8: Multiple Inheritance 3 | 4 | class Pt 5 | attr_accessor :x, :y 6 | def distToOrigin 7 | Math.sqrt(x * x + y * y) 8 | end 9 | end 10 | 11 | class ColorPt < Pt 12 | attr_accessor :color 13 | def darken # error if @color not already set 14 | self.color = "dark " + self.color 15 | end 16 | end 17 | 18 | class Pt3D < Pt 19 | attr_accessor :z 20 | def distToOrigin 21 | Math.sqrt(x * x + y * y + z * z) 22 | end 23 | end 24 | 25 | 26 | # This does not exist in Ruby (or Java/C#, it does in C++) 27 | 28 | # class ColorPt3D_3 < ColorPt, Pt3D 29 | # end 30 | 31 | # two ways we could actually make 3D Color Points: 32 | 33 | class ColorPt3D_1 < ColorPt 34 | attr_accessor :z 35 | def distToOrigin 36 | Math.sqrt(x * x + y * y + z * z) 37 | end 38 | end 39 | 40 | class ColorPt3D_2 < Pt3D 41 | attr_accessor :color 42 | def darken # error if @color not already set 43 | self.color = "dark " + self.color 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /resources/09code/148_mixins.rb: -------------------------------------------------------------------------------- 1 | # Programming Languages, Dan Grossman 2 | # Section 8: Mixins 3 | 4 | module Doubler 5 | def double 6 | self + self # uses self's + message, not defined in Doubler 7 | end 8 | end 9 | 10 | class Pt 11 | attr_accessor :x, :y 12 | include Doubler 13 | def + other 14 | ans = Pt.new 15 | ans.x = self.x + other.x 16 | ans.y = self.y + other.y 17 | ans 18 | end 19 | end 20 | 21 | class String 22 | include Doubler 23 | end 24 | 25 | # these are probably the two most common uses in the Ruby library: 26 | # Comparable and Enumerable 27 | 28 | # you define <=> and you get ==, >, <, >=, <= from the mixin 29 | # (overrides Object's ==, adds the others) 30 | class Name 31 | attr_accessor :first, :middle, :last 32 | include Comparable 33 | def initialize(first,last,middle="") 34 | @first = first 35 | @last = last 36 | @middle = middle 37 | end 38 | def <=> other 39 | l = @last <=> other.last # <=> defined on strings 40 | return l if l != 0 41 | f = @first <=> other.first 42 | return f if f != 0 43 | @middle <=> other.middle 44 | end 45 | end 46 | 47 | # Note ranges are built in and very common 48 | # you define each and you get map, any?, etc. 49 | # (note map returns an array though) 50 | class MyRange 51 | include Enumerable 52 | def initialize(low,high) 53 | @low = low 54 | @high = high 55 | end 56 | def each 57 | i=@low 58 | while i <= @high 59 | yield i 60 | i=i+1 61 | end 62 | end 63 | end 64 | 65 | # here is how module Enumerable could implement map: 66 | # (but notice Enumerable's map returns an array, 67 | # *not* another instance of the class :( ) 68 | # def map 69 | # arr = [] 70 | # each {|x| arr.push x } 71 | # arr 72 | # end 73 | 74 | # this is more questionable style because the mixin is using an 75 | # instance variable that could clash with classes and has to be initialized 76 | module Color 77 | def color 78 | @color 79 | end 80 | def color= c 81 | @color = c 82 | end 83 | def darken 84 | self.color = "dark " + self.color 85 | end 86 | end 87 | 88 | class Pt3D < Pt 89 | attr_accessor :z 90 | # rest of definition omitted (not so relevant) 91 | end 92 | 93 | class ColorPt < Pt 94 | include Color 95 | end 96 | 97 | class ColorPt3D < Pt3D 98 | include Color 99 | end 100 | -------------------------------------------------------------------------------- /resources/09summary.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/resources/09summary.pdf -------------------------------------------------------------------------------- /resources/10summary.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fffxj/Programming-Languages/ccb5f460e38c9ff47c53961fe7ae9cba7517c263/resources/10summary.pdf -------------------------------------------------------------------------------- /resources/README.org: -------------------------------------------------------------------------------- 1 | 2 | + Coursera: [[https://www.coursera.org/learn/programming-languages/][Programming Languages]] 3 | - [[https://www.coursera.org/learn/programming-languages/][Part A, (in SML)]] 4 | - [[https://www.coursera.org/learn/programming-languages-part-b][Part B, (in Racket)]] 5 | - [[https://www.coursera.org/learn/programming-languages-part-c][Part C, (in Ruby)]] 6 | 7 | + Washington: [[https://courses.cs.washington.edu/courses/cse341/][CSE341]] 8 | - [[https://courses.cs.washington.edu/courses/cse341/16sp/][cse341/16sp]] 9 | --------------------------------------------------------------------------------