|
| 1 | +open Printf |
| 2 | + |
| 3 | +(****************************************************************************) |
| 4 | +(* *) |
| 5 | +(* Callbacks (HoF) *) |
| 6 | +(* *) |
| 7 | +(* As a functional programming language, OCaml is perfectly capable of *) |
| 8 | +(* treating functions as first-class citizens; meaning, we can store them *) |
| 9 | +(* inside variables, accept them as parameters, return them from other *) |
| 10 | +(* functions, and more! *) |
| 11 | +(* *) |
| 12 | +(* In programming, a {b callback} is a function that is passed as an *) |
| 13 | +(* argument to a function, which will be later called (with or without *) |
| 14 | +(* arguments) once a certain task is completed synchronously or *) |
| 15 | +(* asynchronously. The term was popularized by the inception of NodeJS's *) |
| 16 | +(* single-threaded concurrent programming but it goes beyond just that... *) |
| 17 | +(* both in history and use cases. A callback can be just a function that *) |
| 18 | +(* represent a strategy, or can be a set of instructions to run at a *) |
| 19 | +(* certain point in time; it can be virtually any type of function, *) |
| 20 | +(* really; as long as it's passed as an argument to another function. *) |
| 21 | +(* *) |
| 22 | +(****************************************************************************) |
| 23 | + |
| 24 | +let with_named_parameters ~callback name = |
| 25 | + let greeting = sprintf "Hello, %s! How are you today?" name in |
| 26 | + printf "HoF 'with_named_parameters' will run callback with [%s]...\n" name; |
| 27 | + callback greeting; |
| 28 | + print_endline |
| 29 | + "Higher-Order Function 'with_named_parameters' finished execution!" |
| 30 | +;; |
| 31 | + |
| 32 | +let _ = |
| 33 | + (* Why have [callback] as a named parameter? |
| 34 | + It's idiomatic in the OCaml world... *) |
| 35 | + with_named_parameters |
| 36 | + ~callback:(fun greeting -> printf "Greeting received: %s\n" greeting) |
| 37 | + "Luis"; |
| 38 | + print_newline () |
| 39 | +;; |
| 40 | + |
| 41 | +(**************************************************************************) |
| 42 | +(* *) |
| 43 | +(* Dificultad Extra (opcional) *) |
| 44 | +(* *) |
| 45 | +(* Crea un simulador de pedidos de un restaurante utilizando callbacks. *) |
| 46 | +(* Estará formado por una función que procesa pedidos. *) |
| 47 | +(* Debe aceptar el nombre del plato, una callback de confirmación, una *) |
| 48 | +(* de listo, y otra de entrega. *) |
| 49 | +(* *) |
| 50 | +(* - Debe imprimir una confirmación cuando empiece el procesamiento. *) |
| 51 | +(* - Debe simular un tiempo aleatorio entre 1 a 10 segundos entre *) |
| 52 | +(* procesos. *) |
| 53 | +(* - Debe invocar a cada callback siguiendo un orden de procesado. *) |
| 54 | +(* - Debe notificar que el plato está listo o ha sido entregado. *) |
| 55 | +(* *) |
| 56 | +(**************************************************************************) |
| 57 | + |
| 58 | +let wait_between_1_and_10_seconds () = Thread.delay (Random.float 9.0 +. 1.0) |
| 59 | + |
| 60 | +module Restaurant : sig |
| 61 | + val show_menu : unit -> unit |
| 62 | + |
| 63 | + val place_order |
| 64 | + : on_confirmed:(unit -> unit) |
| 65 | + -> on_ready:(unit -> unit) |
| 66 | + -> on_delivered:(string -> unit) |
| 67 | + -> int |
| 68 | + -> unit |
| 69 | +end = struct |
| 70 | + Random.self_init () |
| 71 | + |
| 72 | + type dish = |
| 73 | + | HotDog |
| 74 | + | Cheeseburger |
| 75 | + | CaesarSalad |
| 76 | + | Chicken |
| 77 | + | Shortcake |
| 78 | + | Ramen |
| 79 | + [@@deriving show] |
| 80 | + |
| 81 | + let dish_of_int = function |
| 82 | + | 1 -> HotDog |
| 83 | + | 2 -> Cheeseburger |
| 84 | + | 3 -> CaesarSalad |
| 85 | + | 4 -> Chicken |
| 86 | + | 5 -> Shortcake |
| 87 | + | 6 -> Ramen |
| 88 | + | _ -> raise (Invalid_argument "Dish number not in the menu") |
| 89 | + ;; |
| 90 | + |
| 91 | + let dish_to_emoji = function |
| 92 | + | HotDog -> "🌭" |
| 93 | + | Cheeseburger -> "🍔" |
| 94 | + | CaesarSalad -> "🥗" |
| 95 | + | Chicken -> "🍗" |
| 96 | + | Shortcake -> "🍰" |
| 97 | + | Ramen -> "🍜" |
| 98 | + ;; |
| 99 | + |
| 100 | + let show_menu () = |
| 101 | + print_endline "#1 <- Hot dog"; |
| 102 | + print_endline "#2 <- Cheeseburger"; |
| 103 | + print_endline "#3 <- Caesar salad"; |
| 104 | + print_endline "#4 <- Chicken wings"; |
| 105 | + print_endline "#5 <- Strawberry shortcake"; |
| 106 | + print_endline "#6 <- Ramen bowl" |
| 107 | + ;; |
| 108 | + |
| 109 | + let process_order ~on_confirmed ~on_ready ~on_delivered dish = |
| 110 | + wait_between_1_and_10_seconds (); |
| 111 | + print_endline "Order was confirmed, invoking confirmation callback..."; |
| 112 | + on_confirmed (); |
| 113 | + wait_between_1_and_10_seconds (); |
| 114 | + print_endline "Order is ready, invoking readiness callback..."; |
| 115 | + on_ready (); |
| 116 | + wait_between_1_and_10_seconds (); |
| 117 | + print_endline "Order being delived to table, invoking delivery callback..."; |
| 118 | + on_delivered (dish_to_emoji dish) |
| 119 | + ;; |
| 120 | + |
| 121 | + let place_order ~on_confirmed ~on_ready ~on_delivered dish_number = |
| 122 | + let dish = dish_of_int dish_number in |
| 123 | + let order_thread = |
| 124 | + Thread.create (process_order ~on_confirmed ~on_ready ~on_delivered) dish |
| 125 | + in |
| 126 | + printf |
| 127 | + "Restaurant cashier says: Order [%s] has been placed!\n" |
| 128 | + (show_dish dish); |
| 129 | + print_endline "---------------------------------------------------"; |
| 130 | + Thread.join order_thread |
| 131 | + ;; |
| 132 | +end |
| 133 | + |
| 134 | +let _ = |
| 135 | + print_endline "Welcome to my restaurant! Here's the menu:"; |
| 136 | + Restaurant.show_menu (); |
| 137 | + let dish_number = |
| 138 | + Moure.Io.prompt |
| 139 | + ~err_msg:"Invalid number" |
| 140 | + ~f:int_of_string |
| 141 | + "Choose a menu item between 1 and 6: " |
| 142 | + in |
| 143 | + match dish_number with |
| 144 | + | Ok dish_number -> |
| 145 | + Restaurant.place_order |
| 146 | + ~on_confirmed:(fun () -> |
| 147 | + print_endline "Customer says: Time to take a seat :)") |
| 148 | + ~on_ready:(fun () -> |
| 149 | + print_endline "Customer says: Food's ready? I'll be waiting!") |
| 150 | + ~on_delivered:(fun emoji -> |
| 151 | + printf "Customer says: Yum, time to eat my [%s]!\n" emoji) |
| 152 | + dish_number |
| 153 | + | Error msg -> print_endline msg |
| 154 | +;; |
| 155 | + |
| 156 | +(* Output of [dune exec reto21]: |
| 157 | +
|
| 158 | + HoF 'with_named_parameters' will run callback with [Luis]... |
| 159 | + Greeting received: Hello, Luis! How are you today? |
| 160 | + Higher-Order Function 'with_named_parameters' finished execution! |
| 161 | +
|
| 162 | + Welcome to my restaurant! Here's the menu: |
| 163 | + #1 <- Hot dog |
| 164 | + #2 <- Cheeseburger |
| 165 | + #3 <- Caesar salad |
| 166 | + #4 <- Chicken wings |
| 167 | + #5 <- Strawberry shortcake |
| 168 | + #6 <- Ramen bowl |
| 169 | + Choose a menu item between 1 and 6: 2 |
| 170 | + Restaurant cashier says: Order [Reto21.Restaurant.Cheeseburger] has been placed! |
| 171 | + --------------------------------------------------- |
| 172 | + Order was confirmed, invoking confirmation callback... |
| 173 | + Customer says: Time to take a seat :) |
| 174 | + Order is ready, invoking readiness callback... |
| 175 | + Customer says: Food's ready? I'll be waiting! |
| 176 | + Order being delived to table, invoking delivery callback... |
| 177 | + Customer says: Yum, time to eat my [🍔]! |
| 178 | +*) |
0 commit comments