OCaml
- File size
- 13.1KB
- Lines of code
- 393
OCaml
Comments
(* This is a comment *)
Introduction
A strictly evaluated, functional language with imperative features.
(*** FUNCTIONAL LAND ***)
(* everything in OCaml is an expression *)
(* every expression (* variables, functions *) evaluates to a value *)
(* OCaml lacks "procedures" and every function must evaluate to a value, so functions that don't do that and are called for their side effects (* like print_endline *) return a value of "unit" type *)
Expressions, Variables and Functions
- double semicolon language (to separate expressions, though not always necessary)
letfor function and variable declaration- currying and type signatures like haskell
- optional type declaration (OCaml compiler automatically infers types)
(*** EXPRESSIONS ***)
(* Expressions can be separated by a double semicolon, though in production source code often omits the double semicolon for stylistic purposes. *)
(*** VARIABLES ***)
let x = 10;; (* this is a variable *)
(* definitons can be chained together with "let ... in" constructs *)
let x = 10 in
let y = 20 in
x + y ;; (* this expression evaluates to 30 *)
(*** FUNCTIONS ***)
let func_int(x:int):int = x + 1;; (* this is a function *)
(* invoking functions usually doesn't require brackets UNLESS the function argument is an expression *)
let func_int_10 = func_int 10;;
let func_inc_idk = func_int (11-1);;
(* every function must take at least one argument *)
let print_hlo_wrld() = print_endline "hello world";; (* when creating void functions, we need to specify the bracket to denote the empty function *)
print_hlo_wrld();; (* when calling void functions, we must specify the empty bracket *)
(* calling a function with an insufficient number of arguments creates a new function *)
let make_inc x y = x + y;; (* the type signature for make_inc is int -> int -> int *)
let inc_2 = make_inc 2;; (* this creates a new function inc_2 of type signature int -> int *)
inc_2 3;; (* this will evaluate to 5 *)
(* when there are multiple expressions in the function body, the last expression becomes the return value and all other expressions are of "unit" type *)
let print_and_return x =
print_endline (string_of_int x);
x
;;
(*** RECURSIVE FUNCTIONS ***)
(* recursive functions must be marked with a rec*)
let rec factorial n =
if n = 0 then 1
else n * factorial (n-1)
;;
(* note the above can also be written like this *)
let rec factorial n = if n = 0 then 1 else n * factorial (n-1);; (* just that this shit is really virtually unreadable so the above is preferred *)
(* the "let ... and ... in" construct is also available for mutually recursive functions *)
let rec
is_even = function
| 0 -> true
| n -> is_odd(n-1)
and
is_odd = function
| 0 -> false
| n -> is_even(n-1)
;;
(*** ANONYMOUS FUNCTIONS ***)
(* anonymous functions are declared with the fun keyword *)
let my_lambda = fun x -> x * x;;
Operators
(*** OPERATORS ***)
(* in functional programming land, every operator is a function and can be called as such *)
(+) 3 4;; (* evaluates the same way as 3 + 4 *)
(* OCaml DOESN'T ONLY refrain from implict type conversion between floats and integers, float arithmetic uses wholly different operators *)
12 + 3;; (* integer arithmetic, evaluates to 15 *)
12 / 3;; (* integer arithmetic, evalutes to 4 *)
12.0 +. 3.0;; (* float arithmetic, evaluates to 15.0 *)
12.0 /. 3.0;; (* float arithmetic, evaluates to 4.0 *)
5 mod 2;; (* modulo is universal across floats and integers *)
(* Unary minus (* the negation operator *) is a marked exception to this rule as it is polymorphic, although type-specific versions of it do exist as well *)
(* Unary operators are operators which are used to calculate the result on only one operand *)
(* Binary operators are operatores which are used to calculate the result on two operands *)
(* Polymorphic unary operator *)
-3;; (* evaluates to the integer value -3 *)
-4.5;; (* evaluates to the float value -4.5 *)
(* Type specific unary operators *)
~- 3;; (* applicable for integers only *)
~-. 3.4;; (* applicable for floats only *)
~- 3.4;; (* this results in a type error *)
(* You can also be sneaky and redefine your own operators for fun *)
let (~/) x = 1.0 /. x;; (* unary operators must start with ~ *)
~/ 4.0;; (* this evaluates to the float value of 0.25 *)
Data structures
(*** DATA STRUCTURES ***)
(* LIST *)
(* square brackets, items are semi-colon separated *)
(* dynamically allocated space, size can be casually changed *)
(* dynamically allocated space, can store elements of different datat types *)
let my_list = [1;2;3];; (* of type "int list" *)
(* LIST METHODS *)
(* LIST INDEXING *)
List.nth my_list 1;; (* evaluates to integer 2, the second element in the list of index 1 *)
(* LIST MAP *)
(* List.map() calls an anonymous function that is user-defined *)
(* List.map() applies the given function to each iteration variablein the list *)
List.map(fun x -> x * 2) [1;2;3];; (* this should evaulate to [2;4;6] *)
(* LIST FILTER *)
(* List.filter() also calls an anonymous function that is user-defined *)
(* List.filter() applies the specified conditonal check as a function to the list, and only those that pass said check are remaining in the list *)
List.filter (fun x -> x mod 2 = 0) [1;2;3;4];; (* this should evaulate to [2;4] *)
(* ADDING ELEMENTS *)
(* add an item to the FRONT of a list with the :: constructor which is often referred to as a "cons" *)
1 :: [2;3];; (* evaluates to [1;2;3] *)
(* TUPLES *)
(* (* optionally surrounded by *) round brackets, items are comma separated *)
let my_tuple = 3, 4;; (* of type "int * int" *)
let my_other_tuple = (5,6,7);; (* this is a much clearer more approved syntax *)
(* warning to not separate list items by commas, otherwise you'll accidentally create a list with a tuple inside *)
let bad_list = [1,2];; (* this shit becomes [(1,2)] *)
(* ARRAYS *)
(* "[| |]" surrounded, items are semicolon seperated *)
(* statically allocated space, size of array declared at initialization * )
(* statically allocated space, arrays can only contain same data type *)
let my_array = [| 1;2;3 |];;
(* ARRAY INDEXING *)
my_array.(0);; (* this evaluates to the integer 1, the first element of the array with index 0 *)
Strings and Characters
(*** STRINGS and CHARACTERS ***)
(* double quotes for string literals *)
(* single quotes for character literals *)
(* single and double quotes are not interchangeable, effects may vary depending on how you mix them up *)
let my_str = "Hello world";; (* string literal *)
let my_char = 'a';; (* character literal *)
let unintended_effect = "w";; (* this creates a single character string, not a character *)
let syntax_error = 'syntax error';; (* this results in a syntax error *)
(* STRING CONCATENATION *)
(* ^ operator *)
let some_str = "hello" ^ "world";; (* evaluates to "helloworld" string *)
(* CAVEAT *)
(* strings are NOT arrays of characters, and the two data types cannot be mixed in expressions *)
(* characters are converted to strings with String.make 1 my_char *)
let ocaml = (String.make 1 'O') ^ "Caml";; (* this evaluates to the string value "OCaml" by type converting a character to a string and concatenating that single-character string with the "Caml" string *)
(* FORMATTED STRING *)
(* C or Bash-like syntax *)
Printf.printf "%d %s" 99 "bottles of beer";; (* evauates to the string value of "99 bottles of beer" *)
(* PRINT STATEMENTS *)
print_string "hello world\n";; (* prints string values without the newline character *)
print_endline "hello world";; (* prints string with a newline character *)
(* READ STRINGS *)
let line = read_line();; (* does what it says it does *)
User-defined data types
(*** USER-DEFINED DATA TYPES ***)
(* defined using the type keyword *)
type my_int = int;; (* this is an incredibly useless type alias, refrain from doing this *)
(* there are also magic type constructors that must start with a capital letter, do google what these do for more detail *)
type ml = Ocaml | StandardML;;
let lang = OCaml;; (* has the type "ml" *)
(* the below are also valid type constructors *)
type my_number = PlusInfinity | MinusInfinity | Real of float;;
let r0 = Real (-3.4);; (* of type my_number *)
(* type constructors can also be used to implement polymorphoc arithmetic *)
type number = Int of int | Float of float;;
(* an example is of a point on a 2d plane *)
type point2d = Point of float * float;;
let my_point = Point (2.0, 3.0);;
(* types can also be parameterized, like in this type for "list of lists of anything really" where 'a can be substituted with any type *)
type 'a list_of_lists = 'a list list;;
type int_list_list = int list_of_lists;;
(* types can also mysteriously be recursive, like this type analogous to a built-in list of integers *)
type my_int_list = EmptyList | IntList of int * my_int_list;;
let l = IntList (1, EmptyList);;
Pattern matching
(*** PATTERN MATCHING ***)
(* all hail the pattern matching statement *)
(* matches an argument against an exact value, a predicate or a type constructor *)
(* _ acts as the catch-all default statement similar to Rust *)
(* MATCHING against exact values *)
let is_zero x =
match x with
| 0 -> true
| _ -> false
;;
let is_one = function (* the function keyword is interchangeable with the match with *)
| 1 -> true
| _ -> false
;;
(* MATCHING predicates *)
(* basically guarded pattern matching aka pattern matching with a conditional check *)
let abs x =
match x with
| x when x < 0 -> -x
| _ -> x
;;
abs 5;; (* evaluates to 5 *)
abs (-5);; (* also evaluates to 5 *)
(* MATCHING type constructors *)
type animal = Dog of string | Cat of string;;
let say x =
match x with
| Dog x -> x ^ " says woof"
| Cat x -> x ^ " says meow"
;;
say (Cat "Fluffy");; (* evaluates to the string "Fluffy says meow" *)
(* TRAVERSING DATA STRUCTURES with pattern matching *)
(* most commonly used for recursive types *)
(* built-in constructor operator :: covered previously can be matched like any other since its a type constructor *)
let rec sum_list l =
match l with
| [] -> 0
| head :: tail -> head + (sum_list tail)
;;
sum_list [1;2;3];; (* evaluates to 6 *)