In OCaml there are 6 basic types: [int], [float], [char], [string], [bool],
and [unit].
1 2 3 4 5 6 7 8
OCaml type Range
int 63-bit signed int on 64-bit processors, or 31-bit signed int on 32-bit processors float IEEE double-precision floating point, equivalent to C's double bool A boolean, written either 'true' or 'false' char An 8-bit character string A string (sequence of 8 bit chars)
string
String defination and explanation as follows:
1 2 3 4 5 6 7 8 9 10
(* define a string parameter with name first_name *) let first_name = "Fred" (* define a string paramter with name last_name, and type is string. important s is a lower case letter 'name: type =' equals to 'name =' *) let last_name : string = "Flintstone" (* String concatenation is done with the [( ^ )] operator. between two " " has a space. The output for this is val full_name : string = "Fred Flintstone" *) let full_name = first_name ^ " " ^ last_name
(* You can use [( && )] for logical and [( || )] for logical or What does the following do? *) let() = assert (true || a_boolean_false) (** ========== The [unit] Type ========== **) (* [unit] is a special type in OCaml that has only one possible value, written (). It is generally used for mutation and I/O-operations. (I/O stands for input/output. Examples: printing to screen, reading a file, sending and receiving network requests.) *)
let() = Stdio.print_endline "Hi, my name is Fred Flintstone and I am 5 years old"
(* To combine several unit operations together, the [;] operator is used. *) let() = Stdio.print_endline "Hi, my name is "; Stdio.print_endline full_name; Stdio.print_endline " and I am 5 years old" (* When evaluating a unit operation on the toplevel, you should wrap it in a [let] binding, as in {| let () = ... |} This isn't actually necessary in all cases, but it will save you from some frustrating debugging of compiler issues if you just always include it. For this reason, idiomatic OCaml code has [let () = ...] as its entrypoint, similar to the [main] function in languages like C, C++ and Java (although in those languages it is required). *)
(* An aside on printing: Like many other programming languages, you can use formatted strings for printing! We also use the '\n' character for printing newlines. *) let() = Stdio.printf "Hi, my name is %s and I am %d years old\n" full_name 5
(** ========== Inline Tests ========== *) (* The lines that follow are inline tests. Each evaluates a boolean expression. They are run during the build, and failures -- evaluating to false -- are treated like compile errors by the build tool and editors. We will see other kinds of inline tests later, and some interesting patterns for using them. *)
(* An aside on modules and comparison: While OCaml supports polymorphic comparison (i.e. comparison of values of arbitrary types), it is good practice to use equality and comparison functions specific to each type. Types, and functions that operate on those types, are gathered into namespaces that we call "modules". We can access functions inside modules using the syntax [Module.function]. So, [Int.equal] is the [equal] function defined in the [Int] module. Its signature is {| val equal : int -> int -> bool |} In words: [equal] takes two [int]s and returns a [bool]. The following line is applying that function to two inputs, [5] and [int_average 5 5]. *)
(* Modules can also contain operators. By convention, the equality operator is defined and equivalent to the [equal] function. To reference an operator in a module, we need to surround it with parentheses. Try removing the parentheses around the "=" and compiling to see what error you get. [Int.(=)] is the same as [Int.equal]. *)
Instead of using one or more if … then … else … constructs to make choices in OCaml programs, we can use the match keyword to match on multiple possible values. Consider the factorial function:
1 2 3
# letrec factorial n = if n <= 1then1else n * factorial (n - 1);; val factorial : int -> int = <fun>
We can write it using pattern matching instead:
1 2 3 4 5
# letrec factorial n = match n with | 0 | 1 -> 1 | x -> x * factorial (x - 1);; val factorial : int -> int = <fun>
Equally, we could use the pattern _ which matches anything, and write:
1 2 3 4 5
# letrec factorial n = match n with | 0 | 1 -> 1 | _ -> n * factorial (n - 1);; val factorial : int -> int = <fun>
In fact, we may simplify further with the function keyword, which introduces pattern-matching directly:
1 2 3 4
# letrec factorial = function | 0 | 1 -> 1 | n -> n * factorial (n - 1);; val factorial : int -> int = <fun>
We will use pattern matching more extensively as we introduce more complicated data structures.