Wednesday, September 24, 2025

OCAML OBJECT-ORIENTED PROGRAMMING (OOP)

 profile picture

     

 REVISED: Friday, October 3, 2025                                        





OCAML OBJECT-ORIENTED PROGRAMMING (OOP) 

(*
 ocaml C:\AI2025\objective_1.ml


1. OBJECTIVE: Understand the basics of classes and objects in OCaml.
 A 'class' is a blueprint for creating 'objects'. An object is an instance of a class, containing both data (instance variables) and behavior (methods).
*)

(* 'class' is the keyword to define a new class. 'simple_greeter' is the name of our class. *)
class simple_greeter =
  object (self) (* 'object' begins the definition of the class's structure. 'self' is a common name for the object itself, allowing it to refer to its own members. *)

    (* This is an instance variable. It holds data specific to each object. *)
    (* It's initialized to "Hello" for every new object created from this class. *)
    val greeting_message = "Hello"

    (* 'method' defines a behavior for the object. This method is named 'greet'. *)
    (* It takes no arguments and prints the greeting_message to the console. *)
    method greet = print_endline greeting_message

end;; (* The 'end' keyword closes the class definition. The ';;' is used in OCaml to mark the end of a top-level phrase. *)

(* --- Creating and Using an Object --- *)

(* 'new' is the keyword used to create an instance (an object) of a class. *)
(* We are creating an object from the 'simple_greeter' class and naming it 'my_greeter_object'. *)
let my_greeter_object = new simple_greeter;;

(* Now, we call the 'greet' method on our newly created object. *)
(* The '#' symbol is used to invoke a method on an object. *)
(* This will execute the code inside the 'greet' method, printing "Hello". *)
my_greeter_object#greet;;

(* Let's define another class to show that objects have their own state. *)
class customizable_greeter (initial_message : string) =
  object (self)

    (* This is an instance variable, but its initial value is determined by the class constructor's parameter. *)
    val mutable greeting_message = initial_message (* 'mutable' allows the value of this variable to be changed after initialization. *)

    (* A method to print the current greeting. *)
    method greet = print_endline greeting_message

    (* A method to change the greeting message. It takes a new string as an argument. *)
    method set_greeting new_message = greeting_message <- new_message (* The '<-' operator is used to assign a new value to a mutable variable. *)

end;;

(* Create an instance of customizable_greeter with an initial message. *)
let friendly_greeter = new customizable_greeter "Welcome!";;

(* Call the greet method. This will print "Welcome!". *)
friendly_greeter#greet;;

(* Now, let's change the message using the 'set_greeting' method. *)
friendly_greeter#set_greeting "It's nice to see you!";;

(* Call the greet method again. This will now print the new message: "It's nice to see you!". *)
friendly_greeter#greet;;

(* Create a different object from the same class. *)
let formal_greeter = new customizable_greeter "Good day.";;

(* This object has its own independent state. Calling its 'greet' method prints "Good day." *)
formal_greeter#greet;;

(* 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows

OCaml version: The OCaml toplevel, version 5.3.0
Coq-LSP version: 0.2.3
Loading personal and system profiles took 1159ms.
PS C:\Users\User> ocaml C:\AI2025\objective_1.ml
Hello
Welcome!
It's nice to see you!
Good day.
PS C:\Users\User>
*)

(*
  2. OBJECTIVE: Understand inheritance.

  Inheritance allows a new class (a 'subclass' or 'derived class') to be based
  on an existing class (a 'superclass' or 'base class'). The subclass inherits
  the public methods and instance variables of the superclass, and can also
  add new ones or modify existing ones.
*)

(* --- The Superclass (Base Class) --- *)
(* Let's define a general 'animal' class. *)
class animal (initial_name : string) =
  object (self)
    (* Instance variable to store the animal's name. *)
    val name = initial_name

    (* A method to get the animal's name. *)
    method get_name = name

    (* A general method for the sound an animal makes. *)
    method speak = print_endline "The animal makes a sound."
end;;

(* --- The Subclass (Derived Class) --- *)
(* Now, we'll create a 'dog' class that inherits from 'animal'. *)
(* The 'inherit' keyword is used to specify the superclass. *)
(* We pass the 'initial_name' up to the 'animal' constructor. *)
class dog (initial_name : string) =
  object (self)
    (* This line establishes the inheritance relationship. *)
    inherit animal initial_name

    (* Overriding a method from the superclass. *)
    (* The 'dog' class provides its own specific implementation for 'speak'. *)
    (* When 'speak' is called on a 'dog' object, this version will be used. *)
    method! speak = print_endline "Woof! Woof!" (* The '!' after method indicates we know we are overriding a parent method. *)

    (* Adding a new method that only exists in the 'dog' class. *)
    method wag_tail = Printf.printf "%s wags its tail.\n" name (* We can access 'name' because it was inherited from 'animal'. *)
end;;

(* --- Creating and Using Objects --- *)

(* Create an instance of the base class 'animal'. *)
let generic_animal = new animal "Creature";;

(* Call its methods. *)
Printf.printf "The animal's name is %s.\n" generic_animal#get_name;;
generic_animal#speak;; (* Outputs: "The animal makes a sound." *)

(* Create an instance of the subclass 'dog'. *)
let my_dog = new dog "Buddy";;

(* We can call methods inherited from 'animal'. *)
Printf.printf "The dog's name is %s.\n" my_dog#get_name;;

(* When we call 'speak', the dog's overridden version is executed. *)
my_dog#speak;; (* Outputs: "Woof! Woof!" *)

(* We can also call the new method defined only in the 'dog' class. *)
my_dog#wag_tail;; (* Outputs: "Buddy wags its tail." *)

(* The following line would cause a compile-time error because 'wag_tail' is not defined in the 'animal' class. *)
(* generic_animal#wag_tail;; *)

(*
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows

OCaml version: The OCaml toplevel, version 5.3.0
Coq-LSP version: 0.2.3
Loading personal and system profiles took 1061ms.
PS C:\Users\User> ocaml C:\AI2025\objecttive_2.ml
The animal's name is Creature.
The animal makes a sound.
The dog's name is Buddy.
Woof! Woof!
Buddy wags its tail.
PS C:\Users\User>
*)

(*
3. OBJECTIVE: Understand polymorphism and virtual methods.

  Polymorphism means "many forms". In OOP, it's the ability to present      the  same interface for differing underlying forms (data types). A                function  can process objects of different classes if they share a common  superclass or interface.

  A 'virtual' method is a method declared in a base class that can be
  redefined (overridden) by its subclasses. In OCaml, methods are virtual by default.
*)

(* Let's define a 'shape' class. It's an abstract concept. *)
class virtual shape =
  object (self)
    (* A 'virtual' method is like a placeholder. It declares that subclasses
       of 'shape' MUST provide their own implementation for this method. *)
    (* A class containing a virtual method must itself be declared 'virtual',
       and you cannot create a direct instance of it using 'new'. *)
    method virtual area : float

    (* A regular method that can be inherited directly. *)
    method print_info = print_endline "This is a shape."
end;;

(* --- Subclasses Implementing the Virtual Method --- *)

(* A 'circle' class that inherits from 'shape'. *)
class circle (r : float) =
  object (self)
    inherit shape
    val radius = r

    (* We provide a concrete implementation for the 'area' method. *)
    method area = 3.14159 *. radius *. radius

    (* We can also override non-virtual methods. *)
    method! print_info = print_endline "This is a circle."
end;;

(* A 'square' class that also inherits from 'shape'. *)
class square (s : float) =
  object (self)
    inherit shape
    val side = s

    (* We provide the required implementation for 'area' for a square. *)
    method area = side *. side

    method! print_info = print_endline "This is a square."
end;;

(* --- Polymorphism in Action --- *)

(* Create instances of our concrete shape classes. *)
let my_circle = new circle 10.0;;
let my_square = new square 5.0;;

(* We can create a list that holds objects of different types, as long
   as they share a common parent class type ('shape' in this case). *)
(* Note the type annotation: 'shape list'. This tells OCaml to treat all
   elements as 'shape' objects, even though their concrete types are
   'circle' and 'square'. *)
let shapes : shape list = [ (my_circle :> shape); (my_square :> shape) ];;
(* The ':>' is a coercion operator. It explicitly tells the compiler to
   "upcast" the object to its parent type. This is often needed when
   creating collections of polymorphic objects. *)

(* Now, we can write a function that operates on any 'shape' object. *)
(* It doesn't need to know if it's a circle or a square. *)
(* It just needs to know that the object has an 'area' method. *)
let print_shape_area (s : shape) =
  s#print_info; (* This will call the overridden version of print_info for each object. *)
  Printf.printf "Its area is: %f\n" s#area;; (* The correct 'area' method is called at runtime. *)

(* Let's iterate through our list of shapes and apply the function. *)
(* This demonstrates polymorphism. The same function call 'print_shape_area s'
   behaves differently depending on the actual type of 's' at runtime. *)
List.iter print_shape_area shapes;;

(*
  Expected Output:
  This is a circle.
  Its area is: 314.159000
  This is a square.
  Its area is: 25.000000
*)

(*
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows

OCaml version: The OCaml toplevel, version 5.3.0
Coq-LSP version: 0.2.3
Loading personal and system profiles took 3264ms.
PS C:\Users\User> ocaml C:\AI2025\objective_3.ml
This is a circle.
Its area is: 314.159000
This is a square.
Its area is: 25.000000
PS C:\Users\User>
*)

(*
4. OBJECTIVE: Understand encapsulation and access control (private members).

  Encapsulation is the bundling of data with the methods that operate on      that  data. It's also about restricting direct access to some of an object's
  components. This is a key principle for hiding implementation details.

  In OCaml, we can define 'private' methods and instance variables. These can only be accessed from within the object itself, not from the outside.
*)

class bank_account (initial_balance : float) =
  object (self)
    (* This instance variable holds the account balance. *)
    (* It's 'mutable' so it can be changed by deposit and withdraw operations. *)
    val mutable balance = initial_balance

    (* --- Private Method --- *)
    (* 'method private' declares a method that can only be called by other
       methods within this same object ('self'). It cannot be called using
       the '#' operator from outside. *)
    method private log_transaction (description : string) (amount : float) =
      Printf.printf "LOG: %s of amount %f. New balance: %f\n" description amount balance;
      (* This is an internal detail. The outside world doesn't need to know how we log things, only that transactions happen. *)

    (* --- Public Methods (The Public API) --- *)

    (* A method to get the current balance. This provides read-only access. *)
    method get_balance = balance

    (* A method to deposit money. *)
    method deposit amount =
      if amount > 0.0 then
        begin
          balance <- balance +. amount; (* Update the balance. *)
          self#log_transaction "DEPOSIT" amount (* Call the private method to log. *)
        end
      else
        print_endline "Error: Deposit amount must be positive."

    (* A method to withdraw money. *)
    method withdraw amount =
      if amount <= 0.0 then
        print_endline "Error: Withdrawal amount must be positive."
      else if amount > balance then
        print_endline "Error: Insufficient funds."
      else
        begin
          balance <- balance -. amount; (* Update the balance. *)
          self#log_transaction "WITHDRAWAL" amount (* Call the private method. *)
        end
end;;

(* --- Using the Encapsulated Object --- *)

let my_account = new bank_account 100.0;;

(* We can call the public methods. *)
Printf.printf "Initial balance: %f\n" my_account#get_balance;;

my_account#deposit 50.0;;
my_account#withdraw 30.0;;

Printf.printf "Final balance: %f\n" my_account#get_balance;;

(* The following lines would cause COMPILE-TIME ERRORS because we are trying to access private members from outside the object. *)

(* ERROR: You cannot directly access the 'balance' instance variable. *)
(* let b = my_account#balance;; *)

(* ERROR: You cannot directly change the 'balance'. *)
(* my_account#balance <- 500.0;; *)

(* ERROR: 'log_transaction' is a private method and cannot be called from outside. *)
(* my_account#log_transaction "MANUAL EDIT" 10.0;; *)

(* By making the balance variable and the log_transaction method private, the class ensures that the account state can only be changed through the defined 'deposit' and 'withdraw' methods, maintaining its integrity. *)

(*
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows

OCaml version: The OCaml toplevel, version 5.3.0
Coq-LSP version: 0.2.3
Loading personal and system profiles took 1064ms.
PS C:\Users\User> ocaml C:\AI2025\objective_4.ml
Initial balance: 100.000000
LOG: DEPOSIT of amount 50.000000. New balance: 150.000000
LOG: WITHDRAWAL of amount 30.000000. New balance: 120.000000
Final balance: 120.000000
PS C:\Users\User>
*)

   (*
5. OBJECTIVE: Understand parameterized (or generic) classes.

  Sometimes you want to create a class that works with different types of data without rewriting the entire class for each type. A parameterized class allows you to specify a type as a parameter when you create the class.

  In OCaml, this is done using type variables, often denoted with a single
  quote, like 'a, 'b, etc.
*)

(* We define a class 'container' that can hold a value of *any* type. *)
(* The 'a is a type parameter. It's a placeholder for a concrete type
   (like int, string, float, etc.) that will be specified later. *)
class ['a] container (initial_value : 'a) =
  object (self)

    (* The instance variable 'content' will have the type 'a. *)
    (* It is mutable so we can update the value stored in the container. *)
    val mutable content = initial_value

    (* This method returns the value stored in the container. *)
    (* Its return type is 'a, which matches the type of the 'content'. *)
    method get_value : 'a = content

    (* This method takes a new value of type 'a and updates the content. *)
    method set_value (new_value : 'a) =
      content <- new_value

    (* This method takes a function 'f' that transforms a value of type 'a
       into a value of type 'b (another type parameter), and applies it. *)
    method map (f : 'a -> 'b) : 'b =
      f content
end;;

(* --- Using the Parameterized Class with Different Types --- *)

(* 1. Creating a container for an INTEGER. *)
(* Here, the type parameter 'a is instantiated as 'int'. *)
let int_box = new container 42;;

(* Get the integer value. *)
let num : int = int_box#get_value;;
Printf.printf "The integer box contains: %d\n" num;;

(* Update the value with another integer. *)
int_box#set_value 100;;
Printf.printf "The integer box now contains: %d\n" int_box#get_value;;

(* Use the 'map' method to convert the int to a string. *)
(* The function `string_of_int` has type `int -> string`. *)
(* So, 'a is int, and 'b is string. *)
let int_as_string : string = int_box#map string_of_int;;
Printf.printf "The integer as a string: '%s'\n\n" int_as_string;;

(* 2. Creating a container for a STRING. *)
(* This time, the type parameter 'a is instantiated as 'string'. *)
let string_box = new container "OCaml";;

(* Get the string value. *)
let text : string = string_box#get_value;;
Printf.printf "The string box contains: '%s'\n" text;;

(* Update the value. *)
string_box#set_value "is fun!";;
Printf.printf "The string box now contains: '%s'\n" string_box#get_value;;

(* Use the 'map' method to get the length of the string. *)
(* The function `String.length` has type `string -> int`. *)
(* So, 'a is string, and 'b is int. *)
let text_length : int = string_box#map String.length;;
Printf.printf "The length of the string is: %d\n" text_length;;

(*
  Expected Output:

The integer box contains: 42
The integer box now contains: 100
The integer as a string (in new box): '100'
The string box contains: 'OCaml'
The string box now contains: 'is fun!'
The length of the string is: 7
*)

(*
6. CONCLUSION: A summary program bringing together all the OOP concepts.

  (*
 START
 ocaml C:\AI2025\objective_7.ml
*)

 (*
  We will model a simple scenario: a small zoo with different animals.
  This will use:
  - Classes and Objects: To represent individual animals.
  - Inheritance: To model the relationship between a general Animal and specific types like Lion and Parrot.
  - Polymorphism: To treat all animals in a list uniformly, making them speak.
  - Encapsulation: To protect the internal state of an animal (e.g., its hunger level).
  - Parameterized Classes: To create a generic "Enclosure" that can hold any type of animal.
*)

(* --- Base Class: Animal (demonstrates Encapsulation) --- *)
class virtual animal (the_name : string) =
  object (self)
    val name = the_name
    val mutable hunger_level = 5 (* Encapsulated state: private to the object *)

    (* Public method to get the name *)
    method get_name = name

    (* A virtual method that subclasses MUST implement *)
    method virtual speak : unit

    (* A private method for internal logic *)
    method private is_hungry = hunger_level > 3

    (* Public method that uses private state and methods *)
    method feed =
      if self#is_hungry then
        begin
          print_endline (name ^ " eats the food happily.");
          hunger_level <- 0
        end
      else
        print_endline (name ^ " is not hungry right now.")
end;;

(* --- Subclasses: Lion and Parrot (demonstrates Inheritance) --- *)
class lion (the_name : string) =
  object
    inherit animal the_name
    (* Implementing the required virtual method *)
    method speak = print_endline "Roar!"
end;;

class parrot (the_name : string) (phrase : string) =
  object
    inherit animal the_name
    val known_phrase = phrase
    (* Implementing speak in a more specific way *)
    method speak = print_endline (known_phrase ^ "!")
end;;

(* --- Generic Class: Enclosure (demonstrates Parameterized Classes) --- *)
(* This enclosure can hold any type of object, but we'll use it for animals. *)
class ['a] enclosure (initial_occupant : 'a) =
  object
    val mutable occupant = initial_occupant
    method get_occupant = occupant
end;;

(* --- Main Program Logic --- *)
let () = (* The 'let () =' idiom is a standard way to start an executable block of code in OCaml. *)
  print_endline "--- Welcome to the OCaml Zoo! ---";

  (* 1. Creating Objects *)
  (*  A 'let' binding must be followed by 'in' to define the rest of the expression where the new variable is in scope. *)
  let leo = new lion "Leo" in
  (* Added 'in' to continue the chain of definitions, making 'polly' available in the following code. *)
  let polly = new parrot "Polly" "Polly wants a cracker" in

  (* 2. Demonstrating Polymorphism *)
  print_endline "\n--- Let's hear from the animals (Polymorphism) ---";
  (* Added 'in' to make 'all_animals' available to the subsequent expressions. *)
  let all_animals : animal list = [ (leo :> animal); (polly :> animal) ] in

  (* We iterate through the list and call the 'speak' method on each.
     The correct, overridden method is called for each object. *)
  (* Replaced the final ';' in the block with 'in' to connect it to the next part of the program. ';' is used to sequence unit-returning expressions within a block. *)
  List.iter (fun an_animal ->
    Printf.printf "%s says: " an_animal#get_name;
    an_animal#speak
  ) all_animals;

  (* 3. Demonstrating Encapsulation *)
  print_endline "\n--- Feeding time (Encapsulation) ---";
  (* The outside world doesn't know about 'hunger_level'. It just calls 'feed'. *)
  leo#feed;
  leo#feed;

  (* 4. Demonstrating Parameterized Class *)
  print_endline "\n--- Checking the enclosures (Parameterized Class) ---";
  (* We can create an enclosure specifically for a 'lion'. *)
  let lion_enclosure = new enclosure leo in
  let resident_lion = lion_enclosure#get_occupant in
  Printf.printf "The lion enclosure is occupied by %s.\n" resident_lion#get_name;

  (* Or one for a 'parrot'. *)
  let parrot_enclosure = new enclosure polly in
  let resident_parrot = parrot_enclosure#get_occupant in
  Printf.printf "The parrot enclosure is occupied by %s.\n" resident_parrot#get_name;

  print_endline "\n--- End of Program ---";;

(*
 END
 ocaml C:\AI2025\objective_7.ml
*)

Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows

OCaml version: The OCaml toplevel, version 5.3.0
Coq-LSP version: 0.2.3
Loading personal and system profiles took 4389ms.
PS C:\Users\User> ocaml C:\AI2025\objective_7.ml
--- Welcome to the OCaml Zoo! ---

--- Let's hear from the animals (Polymorphism) ---
Leo says: Roar!
Polly says: Polly wants a cracker!

--- Feeding time (Encapsulation) ---
Leo eats the food happily.
Leo is not hungry right now.

--- Checking the enclosures (Parameterized Class) ---
The lion enclosure is occupied by Leo.
The parrot enclosure is occupied by Polly.

--- End of Program ---
PS C:\Users\User>

7. REFERENCES
Bird, R. (2015). Thinking Functionally with Haskell. Cambridge, England: Cambridge University Press.

Davie, A. (1992). Introduction to Functional Programming Systems Using Haskell. Cambridge, England: Cambridge University Press.

Goerzen, J. & O'Sullivan, B. &  Stewart, D. (2008). Real World Haskell. Sebastopol, CA: O'Reilly Media, Inc.

Hutton, G. (2007). Programming in Haskell. New York: Cambridge University Press.

Lipovača, M. (2011). Learn You a Haskell for Great Good!: A Beginner's Guide. San Francisco, CA: No Starch Press, Inc.

Thompson, S. (2011). The Craft of Functional Programming. Edinburgh Gate, Harlow, England: Pearson Education Limited.

Tuesday, September 23, 2025

OCAML FUNCTION COMPOSITION

 

 REVISED: Tuesday, September 23, 2025                                        





 1. OCAML FUNCTION COMPOSITION

(*   ocaml C:\AI2025\func_comp.ml  *)

(* Examples *)

(* Let's define a few simple functions to work with. *)

(* add_one: int -> int
   This function takes an integer 'x' and returns 'x + 1'. *)
let add_one x = x + 1

(* square: int -> int
   This function takes an integer 'x' and returns its square. *)
let square x = x * x

(* to_string_pretty: int -> string
   This function takes an integer and converts it into a descriptive string. *)
let to_string_pretty x = "The final result is: " ^ string_of_int x

(* ---- Method 1: Nested Function Calls (Without Composition) ---- *)

(* This is the traditional way to apply one function's result to another.
   We start with a number, say 5. *)
let initial_value = 5

(* First, we apply add_one to our initial_value. *)
let result1 = add_one initial_value (* result1 is 6 *)

(* Then, we apply square to the result of the previous step. *)
let result2 = square result1 (* result2 is 36 *)

(* Finally, we format the result as a string. *)
let final_string_nested = to_string_pretty result2 (* final_string_nested is "The final result is: 36" *)

(* We can write this more compactly by nesting the calls.
   This can become hard to read from inside-out. *)
let final_string_nested_compact = to_string_pretty (square (add_one 5))

(* ---- Method 2: Using a Custom Composition Operator ---- *)

(* In OCaml, we can define our own infix operators. Let's create one for
   forward function composition, which is a common practice. We'll call it |>
   (the pipe operator), which is now standard in the OCaml ecosystem. *)

(* The 'let (|>) x f = f x' definition creates an infix operator '|>'.
   - 'x' is the value on the left-hand side of the operator.
   - 'f' is the function on the right-hand side.
   - 'f x' applies the function 'f' to 'x'.
   So, '5 |> add_one' is exactly equivalent to 'add_one 5'. *)
let (|>) x f = f x

(* Now, let's perform the same sequence of operations using our pipe operator.
   This creates a data pipeline that is read from left to right, which is
   very natural and intuitive. *)
let final_string_piped =
  5            (* Start with the initial value 5. *)
  |> add_one   (* Pipe 5 into add_one, the result is 6. *)
  |> square    (* Pipe 6 into square, the result is 36. *)
  |> to_string_pretty (* Pipe 36 into to_string_pretty. *)

(* The final result is "The final result is: 36", same as before. *)

(* ---- Method 3: Creating a New Composed Function ---- *)

(* Sometimes you want to create a new function by combining others, without
   applying it to an initial value right away. *)

(* Let's define a standard backward composition operator '>>'.
   '(f >> g) x' means 'g(f(x))'. It first applies f, then g. *)
let (>>) f g x = g (f x)

(* Here, we create a new function 'add_one_then_square_then_format'.
   This function represents the entire sequence of operations.
   The definition reads as: apply add_one, then apply square, then apply to_string_pretty. *)
let add_one_then_square_then_format = add_one >> square >> to_string_pretty

(* Now we can use this new, composed function with any integer. *)
let result_from_composed_function = add_one_then_square_then_format 5
(* This will also produce "The final result is: 36" *)

let another_result = add_one_then_square_then_format 10
(* This will compute to_string_pretty(square(add_one(10)))
   -> to_string_pretty(square(11))
   -> to_string_pretty(121)
   -> "The final result is: 121" *)

(* ---- Printing the results to verify ---- *)

(* We use Printf.printf to print the results to the console. *)
let () =
  Printf.printf "Nested compact result: %s\n" final_string_nested_compact;
  Printf.printf "Piped result:          %s\n" final_string_piped;
  Printf.printf "Composed fn result:    %s\n" result_from_composed_function;
  Printf.printf "Composed fn another:   %s\n" another_result

Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows

OCaml version: The OCaml toplevel, version 5.3.0
Coq-LSP version: 0.2.3
Loading personal and system profiles took 5468ms.
PS C:\Users\User>  ocaml C:\AI2025\func_comp.ml
Nested compact result: The final result is: 36
Piped result:          The final result is: 36
Composed fn result:    The final result is: 36
Composed fn another:   The final result is: 121
PS C:\Users\User>

2. CONCLUSION

There are two main operations we can do with function values: apply them, and compose them. 

The nesting of two or more functions to form a single new function is known as composition.

f (g x) = (f . g) x

Read as f of g is the composition of f with g.

The composition of f and g is a function that first applies g to its argument, then f to the value returned by g. It then returns the return value of f.

The primary purpose of the OCaml function composition . dot operator is to chain functions of the same type. It lets us tie the output of whatever appears on the right of the dot operator to the input of whatever appears on the left of the dot operator as long as they are of the same type.

3. REFERENCES

Bird, R. (2015). Thinking Functionally with Haskell. Cambridge, England: Cambridge University Press.

Davie, A. (1992). Introduction to Functional Programming Systems Using Haskell. Cambridge, England: Cambridge University Press.

Goerzen, J. & O'Sullivan, B. &  Stewart, D. (2008). Real World Haskell. Sebastopol, CA: O'Reilly Media, Inc.

Hutton, G. (2007). Programming in Haskell. New York: Cambridge University Press.

Lipovača, M. (2011). Learn You a Haskell for Great Good!: A Beginner's Guide. San Francisco, CA: No Starch Press, Inc.

Thompson, S. (2011). The Craft of Functional Programming. Edinburgh Gate, Harlow, England: Pearson Education Limited.