Skip to content

Commit 956888c

Browse files
authored
Merge pull request #2209 from luishendrix92/main
#10 - OCaml
2 parents 7c3309d + 2e6ad7b commit 956888c

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
open Printf;;
2+
3+
Random.self_init ()
4+
5+
(*--------------------------------------------------------------------------------------\
6+
| |
7+
| Error Handling - Exceptions |
8+
| |
9+
| When evaluation an expression, OCaml may throw pre-defined exceptions matching |
10+
| the type of failure that happened. Exceptions can also be defined by the user |
11+
| and they may or may not have a value (of any type) attached to it which serves |
12+
| as an information provider for the programmer; to let them know what went wrong. |
13+
| The standard library comes with a small set of exceptions, including a generic |
14+
| one called [Failure] that can be conveniently thrown with [failwith "Message"]. |
15+
| The full set includes the following exceptions and how you may encounter them: |
16+
| |
17+
| - [Failure]: Generic error with great convenience. Carries a [string]. |
18+
| - [Invalid_argument]: Used for invariance checking, meaning, prevents the user |
19+
| of an API to pass an invalid argument that may produce an undesired result. |
20+
| Carries a [string], a message to tell the user why the argument is invalid. |
21+
| - [Not_found]: Thrown when an element we're trying to find in a data structure |
22+
| is not present. For example, trying to find an element [elt] in an ['a list]. |
23+
| - [Match_failure]: Raised when failing a pattern match in a [match] expression. |
24+
| Provides a thruple with the file, row, and column of where it happened. |
25+
| This also only happens if your [match] expressions are non-exhaustive. |
26+
| - [Sys_error]: Raised during IO operations within the Sys module, such as a |
27+
| file we're trying to read not existing. Provides a [string] with details. |
28+
| - [End_of_file]: This is more of a signal than an error, it tells us when we |
29+
| reached the end of a buffer during an [stdin] read operation. |
30+
| - [Division_by_zero]: Integer division by zero raises this, but float division by |
31+
| zero will yield [infinity] which is of type [float]. |
32+
| - [Sys_blocked_io]: Raised when a system IO operation is blocked. |
33+
| - [Assert_failure]: This is one of the most useful ones, it is the foundation of |
34+
| testing libraries. It gives us a thruple containing the file, row, and column |
35+
| where the assertion failure happened. The only way to raise this exception is |
36+
| through the [assert] expression (it's not technically a function), for example: |
37+
| [assert false] will raise the exception, but [assert true] won't. |
38+
| - [Stack_overflow]: Raised when the call-stack is full and the program tries to |
39+
| push another stack frame (function calls). Happens a lot during recursion. |
40+
| |
41+
\-------------------------------------------------------------------------------------*)
42+
43+
let () =
44+
let sky = "red" in
45+
try assert (sky = "blue") with
46+
| Assert_failure (file, line, col) ->
47+
printf "The sky is not blue, it's %s; but who asked?\n" sky;
48+
printf "%s did! Where? On line %d and column %d!\n" file line col
49+
;;
50+
51+
let div_by_zero n =
52+
printf "%d / 0 = " n;
53+
let division = n / 0 in
54+
printf "%d\n" division
55+
;;
56+
57+
(* Output: 10 / 0 = Fatal error: exception Division_by_zero. *)
58+
(* let () = div_by_zero 10 *)
59+
60+
(* Output: [1,2,3,4][4] = Fatal error: exception
61+
Invalid_argument("index out of bounds") *)
62+
(* let () = *)
63+
(* printf "[1,2,3,4][4] = "; *)
64+
(* let x = Array.get [| 1; 2; 3; 4 |] 4 in *)
65+
(* printf "%d\n" x *)
66+
(* ;; *)
67+
68+
(* A cool feature of OCaml is that exceptions can also be pattern-matched. *)
69+
let () =
70+
match 5 / 0 with
71+
| 0 -> print_endline "Division by zero? It's just zero :D"
72+
| exception Division_by_zero ->
73+
print_endline "We can't divide by zero? Meh..."
74+
| _ -> print_endline "Doesn't really matter anyway!"
75+
;;
76+
77+
(* The try/with block happens to be an expression, which evaluates to a value.
78+
Obviously the [unit] or [()] is also a value but means we just executed a
79+
side-effect. This expression does not come with a [finally] clause but it
80+
can be achieved by writing some code after the try/with expression.
81+
82+
There is a pattern in idiomatic OCaml that kept appearing, thus giving birth
83+
to the function [Fun.protect] in the standard library. This function runs an
84+
imperative function (that returns a value) and regardless of whether this
85+
function raised an exception or not, it executes an imperative function
86+
(that returns [unit]). It's very useful for resource cleanup. *)
87+
let () =
88+
let open_db_conn () = print_endline "Db is connected, ready for queries..." in
89+
let close_db_conn () = print_endline "Db is closed, goodbye user..." in
90+
let query_that_maybe_fails i =
91+
if Random.int 10 < 5
92+
then failwith "Db query failed unexpectedly!"
93+
else printf "Db query #%d result: %d\n" i (Random.int 100)
94+
in
95+
try
96+
open_db_conn ();
97+
(* Behind the scenes, [~finally] is run before the value is returned or
98+
an exception that happened is re-raised for the user to handle. *)
99+
Fun.protect ~finally:close_db_conn (fun () ->
100+
for i = 1 to 10 do
101+
query_that_maybe_fails i
102+
done)
103+
with
104+
| Failure msg -> print_endline msg
105+
;;
106+
107+
(*--------------------------------------------------------------------------------------\
108+
| |
109+
| Dificultad Extra (opcional) |
110+
| |
111+
| Crea una función que sea capaz de procesar parámetros, pero que también pueda |
112+
| lanzar 3 tipos diferentes de excepciones (una de ellas tiene que corresponderse |
113+
| con un tipo de excepción creada por nosotros de manera personalizada, y debe ser |
114+
| lanzada de manera manual) en caso de error. |
115+
| |
116+
| - Captura todas las excepciones desde el lugar donde llamas a la función. |
117+
| - Imprime el tipo de error. |
118+
| - Imprime si no se ha producido ningún error. |
119+
| - Imprime que la ejecución ha finalizado. |
120+
| |
121+
\-------------------------------------------------------------------------------------*)
122+
123+
exception Blacklisted of string
124+
125+
let join_dev_club name age =
126+
assert (age >= 21);
127+
let blacklist = [ "Aria Richards"; "Douglas Crockford"; "Terry Davis" ] in
128+
if name = ""
129+
then raise (Invalid_argument "Name must not be an empty string.")
130+
else if List.mem name blacklist
131+
then raise @@ Blacklisted (name ^ " is blacklisted from our club.")
132+
else printf "Welcome to the dev club, %s :)\n" name
133+
;;
134+
135+
let try_to_join name age =
136+
print_endline "--------------------";
137+
print_endline "Execution started.";
138+
begin
139+
try
140+
(* Alternative version:
141+
142+
{[
143+
Fun.protect
144+
~finally:(fun () -> print_endline "Execution finalized")
145+
(fun () ->
146+
join_dev_club name age;
147+
print_endline "No exceptions were thrown!")
148+
]}
149+
*)
150+
join_dev_club name age;
151+
print_endline "No exceptions were thrown!"
152+
with
153+
| Assert_failure _ ->
154+
print_endline
155+
"Exception of type [Assert_failure] was thrown because [age] is NOT \
156+
[>= 21]. Not old enough to join!"
157+
| Invalid_argument msg ->
158+
printf
159+
"Exception of type [Invalid_argument] was thrown with message [%s].\n"
160+
msg
161+
| Blacklisted msg ->
162+
printf
163+
"Exception of type [Blacklisted] (custom) was thrown with message [%s].\n"
164+
msg
165+
end;
166+
print_endline "Execution finalized."
167+
;;
168+
169+
let () =
170+
try_to_join "" 35;
171+
try_to_join "Luis" 18;
172+
try_to_join "Aria Richards" 43;
173+
try_to_join "Brais Moure" 38
174+
;;
175+
176+
(* Output of [dune exec reto10]:
177+
178+
The sky is not blue, it's red; but who asked?
179+
bin/reto10.ml did! Where? On line 45 and column 6!
180+
We can't divide by zero? Meh...
181+
Db is connected, ready for queries...
182+
Db is closed, goodbye user...
183+
Db query failed unexpectedly!
184+
--------------------
185+
Execution started.
186+
Exception of type [Invalid_argument] was thrown with message [Name must not be an empty string.].
187+
Execution finalized.
188+
--------------------
189+
Execution started.
190+
Exception of type [Assert_failure] was thrown because [age] is NOT [>= 21]. Not old enough to join!
191+
Execution finalized.
192+
--------------------
193+
Execution started.
194+
Exception of type [Blacklisted] (custom) was thrown with message [Aria Richards is blacklisted from our club.].
195+
Execution finalized.
196+
--------------------
197+
Execution started.
198+
Welcome to the dev club, Brais Moure :)
199+
No exceptions were thrown!
200+
Execution finalized.
201+
*)

0 commit comments

Comments
 (0)