{"id":13620865,"url":"https://github.com/ramonxm/functional-programming","last_synced_at":"2025-04-12T17:11:04.805Z","repository":{"id":109767257,"uuid":"496015194","full_name":"ramonxm/functional-programming","owner":"ramonxm","description":"🔢 |  Repository applying functional programming.","archived":false,"fork":false,"pushed_at":"2023-05-15T12:35:41.000Z","size":627,"stargazers_count":13,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-26T11:39:21.186Z","etag":null,"topics":["functional-programming","paradigms","patterns"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ramonxm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-05-24T23:12:07.000Z","updated_at":"2024-07-06T23:25:51.000Z","dependencies_parsed_at":"2023-06-11T16:31:14.059Z","dependency_job_id":null,"html_url":"https://github.com/ramonxm/functional-programming","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ramonxm%2Ffunctional-programming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ramonxm%2Ffunctional-programming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ramonxm%2Ffunctional-programming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ramonxm%2Ffunctional-programming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ramonxm","download_url":"https://codeload.github.com/ramonxm/functional-programming/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248602313,"owners_count":21131616,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-programming","paradigms","patterns"],"created_at":"2024-08-01T21:01:00.155Z","updated_at":"2025-04-12T17:11:04.769Z","avatar_url":"https://github.com/ramonxm.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# Functional Programming\n\n\u003caside\u003e\n🔥 Functional programming is a programming paradigm that emphasizes using functions as the foundation of software. Functions are treated as first-class citizens, meaning they can be assigned to variables, passed as arguments, and returned as values.\n\n\u003c/aside\u003e\n\nThis allows programmers to write independent functions that can solve complex problems and combine them together. Functions should have no side effects and emphasize immutability, making code more predictable and easier to reason about. Recursion is also essential, allowing functions to call themselves in a more elegant way than iterative loops. Popular functional programming languages include Haskell, Lisp, and Clojure, and many modern programming languages also have functional programming features. However, functional programming has a steep learning curve and may not be suitable for all tasks.\n\n## Differences between imperative and declarative\n\n| Imperative               | Declarative               |\n| ------------------------ | ------------------------- |\n| focus on the flow        | focus on logic            |\n| mutable states           | immutability              |\n| as                       | what                      |\n| greater amount of code   | less amount of code       |\n| low level of scalability | high level of scalability |\n| better known             | less known                |\n| more explicit            | less explicit             |\n\n## Examples\n\n### Imperative Paradigm\n\nThe imperative paradigm is a programming paradigm that focuses on describing how a program should achieve its goals by giving explicit instructions, or commands, that modify the program state. In the imperative paradigm, a program is seen as a sequence of instructions that manipulate variables and control structures in order to achieve a specific result.\n\nIn an imperative program, the programmer specifies a sequence of operations that must be executed in order to achieve a desired result. These operations are typically expressed using control structures such as loops, conditionals, and subroutines, and they can modify the program state by changing the values of variables, reading or writing to input/output devices, or calling other functions.\n\nHere's an example of an imperative program that prints the first 10 even numbers:\n\n```jsx\nfor (let i = 0; i \u003c 20; i++) {\n  if (i % 2 === 0) {\n    console.log(i);\n  }\n}\n```\n\nIn this example, the program uses a **`for`** loop to iterate over the numbers from 0 to 19. For each number, it checks whether it is even using an **`if`** statement, and if so, it prints it to the console using the **`console.log`** function.\n\nThe imperative paradigm is widely used in programming, especially in systems programming, where performance and control over low-level hardware are important. However, it can lead to code that is difficult to read and maintain, especially as programs become more complex.\n\nOne of the main challenges of the imperative paradigm is managing the program state and ensuring that it remains consistent as the program executes. Imperative programs can also be prone to bugs, as the programmer must ensure that the sequence of instructions is correct and does not produce unexpected behavior.\n\n### Declarative Paradigm\n\nThe declarative paradigm is a programming paradigm that focuses on describing what a program should achieve, rather than how to achieve it. In the declarative paradigm, a program is expressed in terms of a set of rules or constraints that define the desired output or behavior.\n\nIn a declarative program, the programmer specifies a set of rules or constraints that define the problem domain, and a set of operations or transformations that should be applied to the data in order to produce the desired output. The program does not specify the sequence of operations, but rather the relationships between data elements and the desired outcomes.\n\nOne of the key benefits of the declarative paradigm is that it allows the programmer to focus on the problem domain and the desired outcomes, rather than the details of how to achieve those outcomes. This can make programs easier to read, maintain, and reason about, especially as programs become more complex.\n\nHere's an example of a declarative program that calculates the sum of an array of numbers:\n\n```jsx\nconst numbers = [1, 2, 3, 4, 5];\n\nconst sum = numbers.reduce((accumulator, currentValue) =\u003e accumulator + currentValue, 0);\n\nconsole.log(sum); // 15\n```\n\nIn this example, the program expresses the desired outcome in terms of a single operation, the **`reduce()`** function, which takes an array of numbers and returns their sum. The program does not specify the sequence of operations that should be used to achieve this outcome, but rather relies on the built-in **`reduce()`** function to do the work.\n\nThe declarative paradigm is widely used in functional programming, where programs are expressed in terms of data transformations and operations on immutable data structures. It is also used in logic programming, where programs are expressed in terms of logical rules and constraints that define the relationships between data elements.\n\nOne of the main challenges of the declarative paradigm is that it can be less intuitive for programmers who are used to thinking in terms of imperative programs. Declarative programs can also be less efficient than imperative programs, as they may require more computation to achieve the same result. However, the benefits of declarative programming, such as increased readability and maintainability, often outweigh these drawbacks in many contexts.\n\n---\n\n## First Class Functions\n\nIn programming languages that support first-class functions, functions are treated as first-class citizens, meaning they can be treated just like any other value in the language, such as a number or a string. This means that functions can be assigned to variables, passed as arguments to other functions, and returned as values from functions.\n\nHere's an example of assigning a function to a variable in JavaScript:\n\n```jsx\nconst add = function(a, b) {\n  return a + b;\n}\n```\n\nIn this example, the **`add`** function is defined using a function expression and then assigned to the **`add`** variable. The **`add`** variable can now be used just like any other variable in the program.\n\nFirst-class functions enable a wide range of programming techniques, such as higher-order functions, which are functions that take other functions as arguments or return functions as values. Higher-order functions are used extensively in functional programming, and can simplify many programming tasks by allowing for generic and reusable code.\n\nHere's an example of using a higher-order function to implement a map function in JavaScript:\n\n```jsx\nconst map = function(array, transform) {\n  const result = [];\n  for (const element of array) {\n    result.push(transform(element));\n  }\n  return result;\n}\n\nconst numbers = [1, 2, 3, 4, 5];\n\nconst doubled = map(numbers, function(n) {\n  return n * 2;\n});\n\nconsole.log(doubled); // [2, 4, 6, 8, 10]\n\n```\n\nIn this example, the **`map`** function takes an array and a transformation function as arguments, and returns a new array with the transformed values. The transformation function is a higher-order function, which takes an element of the array as input and returns a transformed value. The **`map`** function then applies this transformation to each element of the array, using a loop, and returns the result.\n\n## Composition of Functions\n\nFunction composition is a technique that allows you to create a new function by combining two or more existing functions. This can be achieved using a variety of approaches, including chaining function calls, using the **`apply`** or **`call`** methods, and using higher-order functions.\n\nHere's an example of function composition using chaining function calls:\n\n```jsx\nfunction add(a, b) {\n  return a + b;\n}\n\nfunction multiply(a, b) {\n  return a * b;\n}\n\nfunction addThenMultiply(a, b, c) {\n  return multiply(add(a, b), c);\n}\n\nconst result = addThenMultiply(2, 3, 4);\nconsole.log(result); // 20\n```\n\nIn this example, we have three functions - **`add`**, **`multiply`**, and **`addThenMultiply`**. The **`addThenMultiply`** function combines the **`add`** and **`multiply`** functions by chaining their calls together. The result of the **`add`** function, which is the sum of **`a`** and **`b`**, is passed as the first argument to the **`multiply`** function, along with **`c`**. The final result is the product of the sum of **`a`** and **`b`** and **`c`**.\n\nAnother approach to function composition in JavaScript is to use higher-order functions. Here's an example:\n\n```jsx\nfunction add(a, b) {\n  return a + b;\n}\n\nfunction multiply(a, b) {\n  return a * b;\n}\n\nfunction compose(fn1, fn2) {\n  return function(a, b, c) {\n    return fn2(fn1(a, b), c);\n  };\n}\n\nconst addAndMultiply = compose(add, multiply);\nconst result = addAndMultiply(2, 3, 4);\nconsole.log(result); // 20\n```\n\nIn this example, we define a new function called **`compose`**, which takes two functions as arguments and returns a new function that combines them. The **`addAndMultiply`** function is created by passing **`add`** and **`multiply`** to **`compose`**. When **`addAndMultiply`** is called with the arguments **`(2, 3, 4)`**, the output of **`add`** is passed as the first argument to **`multiply`**, resulting in the value **`20`**.\n\n---\n\n## Why was functional programming adopted late?\n\nThe main culprit: **Memory**\n\n- Functional programming was created in 1957, before Structured Programming and Object-Oriented Programming.\n- However, functional programming was not viable at the time, due to the high cost of memory.\n\n## Value vs Reference\n\n```java\nint a = 2;\nint b = a;\n\n```\n\nIn this case, the program is using more memory than necessary because a copy of the value was made, even though there was space available.\n\nThe decision to make a copy of the value was taken because an integer value is a basic and primitive type that consumes little memory. Therefore, it is more logical to generate a copy of the value than to make the same variable point to the same memory address.\n\n```java\nObject a = new Object();\n\n```\n\nWhen dealing with objects, things change a bit. When we create an object, it usually occupies a large space in memory.\n\nIn a variable, the content of the object is not stored, but rather a reference to the location where the object is in memory. For example, if we create an object B that receives the value of object A, the reference to the location in memory is copied.\n\nThis means that we will have two variables pointing to the same location in memory.\n\n---\n\n## PLoP\n\n- Place-Oriented Programming;\n- New information replaces the old;\n- Arose from a limitation in early computing\n- Limited RAM and disk space\n\n1940 → I invented a bit of memory\n\n1953 → I invented a byte of memory\n\n1966 → 1K\n\n1978 → 32k\n\n2011 → Look, 100 terabytes\n\n2038 → What memory?\n\n## Temporal Coupling\n\nTemporal coupling is a programming concept that refers to the degree to which two parts of a program depend on each other's runtime. In other words, temporal coupling describes how one part of the program is linked to another part of the program in terms of runtime.\n\nA common example of temporal coupling is when a piece of code waits for another piece of code to finish executing before continuing. This can lead to performance and reliability issues, especially if the runtime of one part of the program cannot be accurately predicted.\n\nTo minimize temporal coupling, it is recommended to divide the program into independent and well-defined modules that can run autonomously without depending on the runtime of other modules. This can help improve the efficiency, scalability, and maintainability of the program.\n\n## Why switch?\n\n- Functional programming is simpler;\n- It makes it easier to write and maintain code;\n- It has no temporal coupling;\n- Few concurrency issues;\n- It works with immutability.\n\n## How Javascript Works\n\n\u003caside\u003e\n🔥 A session on how JavaScript works can focus on three important aspects of its runtime environment: the heap, call stack, and event queue.\n\n\u003c/aside\u003e\n\n### HEAP\n\n\u003caside\u003e\n🔥 The heap is the memory space where objects are stored in JavaScript. When you declare a new variable or object in your code, it is allocated memory on the heap.\n\n\u003c/aside\u003e\n\n![Untitled](images/Untitled.png)\n\nIn Javascript, the heap is the part of the computer memory where objects are stored. It is managed by the garbage collector of Javascript and is responsible for allocating and deallocating memory for dynamically created and removed objects during program execution.\n\nThe Javascript heap is where values of objects, arrays, functions, and other complex data types are stored. When an object is created, it is allocated in the heap and can be accessed through a reference. When the object is no longer needed, the garbage collector releases the memory occupied by it, marking it as \"collectible.\"\n\nEfficient management of the heap is important to ensure that a Javascript program runs efficiently and without errors. It is important to avoid memory leaks that occur when objects are allocated in the heap but are never removed, occupying unnecessary space and making the program slow and unstable. Excessive use of the heap can also lead to performance problems, as the garbage collector needs to spend more time managing the allocated memory.\n\n### STACK\n\n\u003caside\u003e\n🔥 The call stack is a data structure that keeps track of the current position in your code. Whenever a function is called, its context is pushed onto the stack. When a function returns, its context is popped off the stack, and execution resumes at the previous position.\n\n\u003c/aside\u003e\n\n![Untitled](images/Untitled%201.png)\n\nIn Javascript, the stack is a data structure used to store information about code execution. It is a stack that contains a series of frames, each of which represents an execution context.\n\nThe stack is used to track the order of function execution. Whenever a function is called, a new frame is added to the stack. This frame contains information about the function, including its name, parameters, and local variables. When the function returns, the frame is removed from the stack, and execution continues from the point where the function was called.\n\nThe stack is important to ensure that the code is executed in the correct order. It is used to control the execution of functions and ensure that the local variables of one function do not interfere with the variables of another function that is being executed simultaneously. In addition, the stack is used to detect execution errors, such as stack overflows, which occur when the amount of information stored in the stack exceeds the maximum limit allowed by the execution environment.\n\nIn summary, the stack is a crucial data structure in the execution of Javascript code, and its proper use is important to ensure that the code is executed efficiently and without errors.\n\n### EVENT QUEUE\n\n\u003caside\u003e\n🔥 The event queue is a list of events that have been triggered but are waiting to be processed. JavaScript is single-threaded, meaning that it can only execute one task at a time. When an event occurs, such as a click or a network request, it is added to the event queue. The event loop continuously checks the event queue for new events and processes them one by one.\n\n\u003c/aside\u003e\n\n![Untitled](images/Untitled%202.png)\n\nThe Event Queue is an important part of Javascript's asynchronous programming model. It is a queue (or list) of events (or tasks) waiting to be executed. Each event in the queue is associated with a callback function that will be executed when the event is processed.\n\nEvents in the queue are added whenever an asynchronous action occurs in the program, such as an AJAX request, an animation, a timer event, or user input. When an event is added to the queue, it waits until the execution stack is empty to be processed.\n\nThe processing of events in the queue is done by Javascript's event loop. The event loop is responsible for continuously checking if there are events in the queue, and if so, executing them in order. It takes the first event in the queue and executes its callback function until the function returns. Then it takes the next event in the queue and repeats the process.\n\nThe use of the event queue allows Javascript to execute asynchronous tasks without blocking synchronous code execution. This is important to ensure that the user interface does not freeze while the code performs time-consuming tasks or waits for external events. In addition, the use of callbacks allows asynchronous code to be executed in a non-linear fashion, which is essential for developing complex and responsive applications.\n\nIn summary, the event queue is a fundamental part of Javascript's asynchronous programming model, and its proper use is important to ensure that the code is executed efficiently and responsively.\n\n\u003caside\u003e\n🔥 Understanding these three components is essential to understanding how JavaScript works at a fundamental level. It helps developers write more efficient and effective code, by knowing how memory is allocated, how the call stack operates, and how event-driven programming works in JavaScript.\n\n\u003c/aside\u003e\n\n---\n\n# Understanding Functions in JavaScript\n\n## Function Expression\n\nA function expression is a way to define a function as a value assigned to a variable. This allows the function to be passed around and used in a flexible manner, much like any other data type.\n\nHere's an example of a function expression in JavaScript:\n\n```jsx\nfunction add(a, b) {\n  return a + b;\n};\n\nconst result = add(2, 3); // result is 5\n```\n\nIn this example, we define a function **`add`** using the function expression syntax.\n\n## Anonymous Function\n\nAn anonymous function is a function that has no name and is not bound to any identifier. Instead, it is usually defined as a function expression, which is a way to define a function as a value assigned to a variable.\n\nHere's an example of an anonymous function defined using a function expression:\n\n```jsx\nconst sum = function(a, b) {\n  return a + b;\n};\n\nconst result = sum(2, 3); // result is 5\n```\n\nIn this example, we define a function expression that adds two numbers together and assigns it to the variable **`sum`**. Because the function has no name, it is referred to as an anonymous function.\n\n## Immediately Invoked Function Expression (IIFE)\n\nAn Immediately Invoked Function Expression (IIFE) in JavaScript is a function that is executed immediately after it is defined, without the need for a separate function call. It is a common pattern used in JavaScript to create a private scope for code, prevent naming conflicts, and control the timing of code execution.\n\nHere's an example of an IIFE:\n\n```jsx\n (function() {\n  console.log('This code is executed immediately.');\n})();\n```\n\nIn this example, we define an anonymous function inside a pair of parentheses, and immediately invoke it by adding another pair of parentheses after the function definition. The function body contains a call to **`console.log`**, which prints a message to the console.\n\n## Arrow Function\n\nArrow function is an anonymous JavaScript function introduced in ES6 (ECMAScript 2015) that uses the \"arrow\" syntax (=\u003e) to define a function more succinctly and with a different variable scope than traditional functions.\n\nThe basic syntax of an arrow function is as follows:\n\n```jsx\nconst arrow = () =\u003e console.log('Arrow Function')\narrow()\n\nconst greeting = name =\u003e `Hello ${name}`\n\nconst sum = (...numbers) =\u003e {\n\tlet total = 0\n\tfor(let n of numbers) {\n\t\ttotal += n\n\t}\n\treturn total\n}\n\nconsole.log(sum(10,30,40,50))\n\nconst pow = base =\u003e exp =\u003e Math.pow(base,exp)\n\nconsole.log(pow(2)(4))\n\n// this\nArray.prototype.log = function() {\n\tconsole.log(this)\n}\n\nArray.prototype.last = () =\u003e {\n\tconsole.log(this[0])\n} // not work\n\nconst numbers = [1, 2, 3]\n\nnumbers.log()\n\nnumbers.last() // undefined\n```\n\nAn arrow function can have zero or more parameters enclosed in parentheses, and the function body is enclosed in curly braces ({}) if there is more than one statement, or it can be a single expression if there is only one statement. Additionally, the **`return`** keyword does not need to be explicitly used to return a value, since the returned value is implicitly the result of the last evaluated expression.\n\nAnother important difference is that arrow functions do not have their own **`this`** object, which means that the value of **`this`** is inherited from the context where the function was defined, instead of being defined in the function call. This can be useful in cases where the value of **`this`** needs to be preserved, such as in callback functions.\n\nFinally, arrow functions are generally more concise and easier to read than traditional functions, especially when it comes to simple functions or single returns.\n\n## Constructor Functions\n\nConstructor functions in JavaScript are a way to create objects that share the same properties and methods. They are typically defined with a capitalized name to differentiate them from regular functions.\n\nHere's an example of a constructor function that creates a **`Person`** object with a **`name`** property and a **`greet`** method:\n\n```jsx\nfunction Person(name) {\n  this.name = name;\n}\n\nPerson.prototype.greet = function() {\n  console.log('Hello, my name is ' + this.name);\n}\n```\n\nIn this example, we define a **`Person`** constructor function that takes a **`name`** parameter and sets it as the **`name`** property on the newly created object using the **`this`** keyword. We also define a **`greet`** method on the **`Person`** prototype, which logs a greeting message to the console with the object's **`name`** property.\n\nTo create a new **`Person`** object, we can use the **`new`** keyword to call the **`Person`** constructor:\n\n```jsx\nconst john = new Person('John');\njohn.greet(); // Output: Hello, my name is John\n```\n\nIn this example, we create a new **`Person`** object called **`john`** with the name 'John'. We then call the **`greet`** method on the **`john`** object, which logs a greeting message to the console with the object's **`name`** property.\n\n## Getter Functions\n\nGetter functions in JavaScript are used to retrieve the value of an object's property. They allow you to define a method that is called when a property is accessed, rather than simply returning the value of the property directly.\n\nTo define a getter function, you use the **`get`** keyword followed by the name of the property, and then define a function that returns the value of the property. Here's an example:\n\n```jsx\nconst person = {\n  firstName: 'John',\n  lastName: 'Doe',\n  get fullName() {\n    return this.firstName + ' ' + this.lastName;\n  }\n};\n\nconsole.log(person.fullName); // Output: \"John Doe\"\n```\n\nIn this example, we define an object called **`person`** with two properties: **`firstName`** and **`lastName`**. We then define a getter function called **`fullName`** using the **`get`** keyword. When we access the **`fullName`** property, the getter function is called and returns the concatenation of **`firstName`** and **`lastName`**.\n\nGetter functions can be useful when you want to calculate a property value on the fly, or when you want to perform some kind of validation or transformation on a property value before returning it. They also allow you to create properties that appear to be read-only, since there is no direct way to set the value of a getter property.\n\nHere's an example of using a getter function to calculate the area of a circle based on its radius:\n\n```jsx\nconst circle = {\n  radius: 5,\n  get area() {\n    return Math.PI * this.radius * this.radius;\n  }\n};\n\nconsole.log(circle.area); // Output: 78.53981633974483\n```\n\nIn this example, we define an object called **`circle`** with a **`radius`** property and a **`get`** function called **`area`**. When we access the **`area`** property, the getter function is called and calculates the area of the circle using the formula **`πr^2`**.\n\n## Hoisting\n\nHoisting in JavaScript is a behavior where variable and function declarations are moved to the top of their respective scopes during the compilation phase, before the code is executed. This means that variables and functions can be used before they are declared.\n\nHowever, it's important to note that only the declarations are hoisted, not the assignments. This means that while you can use a variable or function before it is declared, any assignments or function expressions will not be hoisted and will result in a reference error.\n\nHere's an example of hoisting in action:\n\n```jsx\nconsole.log(myName); // Output: undefined\nvar myName = \"John\";\n```\n\nIn this example, we declare a variable called **`myName`** and then immediately log it to the console. However, since the declaration is hoisted to the top of the scope, the value of **`myName`** is **`undefined`** at the time of the **`console.log`** statement.\n\nHere's another example with a function declaration:\n\n```jsx\ngreet(\"John\"); // Output: \"Hello John\"\n\nfunction greet(name) {\n  console.log(\"Hello \" + name);\n}\n```\n\nIn this example, we define a function called **`greet`** and then immediately call it with a name argument. Since the function declaration is hoisted to the top of the scope, the call to **`greet`** is valid and will output \"Hello John\" to the console.\n\n---\n\n## Callback\n\n![Callback-notitle.svg](images/Callback-notitle.svg)\n\nIn programming, a callback is a function that is passed as an argument to another function and will be executed when a certain event occurs or when the original function finishes its execution.\n\nA common example of using callbacks is in asynchronous functions, such as in HTTP requests. When a request is made, it is sent to the server and the function does not wait for the response, continuing to execute the rest of the code. When the response is received, the callback function is called, allowing the code to handle the response data.\n\nAnother example is in user interface, where a callback function can be used to respond to user input events, such as clicking a button or typing text in an input box.\n\nThe use of callbacks is an important technique in asynchronous programming and helps make the code more modular and flexible.\n\n- Examples:\n\n```jsx\nfunction exec(fn, a, b) {\n\treturn fn(a,b)\n}\n\nconst sum = (x,y) =\u003e console.log(x + y)\n\nconst subtract = (w,z) =\u003e console.log(w -z)\n\nexec(sum, 30, 40)\n\nconst fn = () =\u003e console.log('interval')\nsetInterval(fn, 1000)\n```\n\n```jsx\nconst fs = require('fs')\nconst path = require('path')\n\nconst myPath = path.join(__dirname, 'data.txt')\n\nfunction showContent(err, data) {\n\tconsole.log(data)\n}\n\nconsole.log('Start..')\nfs.readFile(myPath, {}, showContent)\nconsole.log('End..')\n\nconsole.log('Start Sync')\nconst content = fs.readFileSync(myPath)\nconsole.log(content)\nconsole.log('End Sync')\n\n```\n\n## Map\n\nThe **`map()`** function in JavaScript is a method that is available on arrays. It creates a new array by applying a callback function to each element of the original array.\n\nThe syntax of the **`map()`** function is as follows:\n\n```jsx\narray.map(callback(currentValue[, index[, array]])[, thisArg])\n```\n\nThe **`callback`** function is called for each element in the array, and it can take up to three arguments:\n\n- **`currentValue`**: The value of the current element being processed in the array.\n- **`index`** (optional): The index of the current element being processed in the array.\n- **`array`** (optional): The original array on which **`map()`** was called.\n\nThe **`callback`** function should return a new value for each element in the array. The **`map()`** function will then create a new array with the same length as the original array, where each element is the result of calling the **`callback`** function on the corresponding element in the original array.\n\nHere's an example of using **`map()`** to create a new array of numbers that are twice the values of the original array:\n\n```jsx\n\nconst numbers = [1, 2, 3, 4, 5];\n\nconst doubledNumbers = numbers.map((num) =\u003e {\n  return num * 2;\n});\n\nconsole.log(doubledNumbers); // [2, 4, 6, 8, 10]\n```\n\nIn this example, the **`map()`** function is called on the **`numbers`** array, and a callback function is passed to it that takes each element in the array and returns a new value that is twice the original value. The **`map()`** function then creates a new array (**`doubledNumbers`**) where each element is the result of calling the callback function on the corresponding element in the original array.\n\nThe **`map()`**function is a useful tool for transforming data in an array into a new array of transformed data. It can be used in a variety of scenarios, including filtering out certain elements, transforming strings into numbers, and much more.\n\nBelow is the implementation of `map`:\n\n```jsx\nArray.prototype.newMap = function(fn) {\n\tconst mapped = []\n\tfor(let i = 0; i \u003c this.length; i++) {\n\t\tconst result = fn(this[i])\n\t\tmapped.push(result)\n\t}\n\treturn mapped\n}\n```\n\n## Filter\n\nThe **`filter()`** function in JavaScript is a method that is available on arrays. It creates a new array with all elements that pass the test implemented by the provided callback function.\n\nThe syntax of the **`filter()`** function is as follows:\n\n```jsx\narray.filter(callback(currentValue[, index[, array]])[, thisArg]\n```\n\nThe **`callback`** function is called for each element in the array, and it can take up to three arguments:\n\n- **`currentValue`**: The value of the current element being processed in the array.\n- **`index`** (optional): The index of the current element being processed in the array.\n- **`array`** (optional): The original array on which **`filter()`** was called.\n\nThe **`callback`** function should return **`true`** if the current element passes the test, and **`false`** otherwise. The **`filter()`** function will then create a new array with all elements for which the **`callback`** function returned **`true`**.\n\nHere's an example of using **`filter()`** to create a new array of even numbers from an original array of numbers:\n\n```jsx\nconst numbers = [1, 2, 3, 4, 5];\n\nconst evenNumbers = numbers.filter((num) =\u003e {\n  return num % 2 === 0;\n});\n\nconsole.log(evenNumbers); // [2, 4]\n```\n\nIn this example, the **`filter()`** function is called on the **`numbers`** array, and a callback function is passed to it that takes each element in the array and returns **`true`** if the element is even, and **`false`** otherwise. The **`filter()`** function then creates a new array (**`evenNumbers`**) with all elements from the original array that returned **`true`** when passed to the callback function.\n\nThe **`filter()`** function is a useful tool for filtering data in an array based on a condition. It can be used in a variety of scenarios, including selecting certain elements, removing unwanted elements, and much more.\n\nBelow is the implementation of `filter`:\n\n```jsx\nArray.prototype.newFilter = function(fn) {\n\tconst newArray = []\n\tfor(let i = 0; i \u003c this.length; i++) {\n\t\tif(fn(this[i], i, this)) {\n\t\tnewArray.push(this[i])\n\t\t}\n\t}\n\treturn newArray;\n}\n```\n\n## Reduce\n\nThe **`reduce()`** function in JavaScript is a method that is available on arrays. It is used to reduce an array to a single value by applying a callback function to each element of the array.\n\nThe syntax of the **`reduce()`** function is as follows:\n\n```jsx\narray.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])\n```\n\nThe **`callback`** function is called for each element in the array, and it can take up to four arguments:\n\n- **`accumulator`**: The accumulated value computed by the previous invocation of the **`callback`** function or the **`initialValue`**.\n- **`currentValue`**: The value of the current element being processed in the array.\n- **`index`** (optional): The index of the current element being processed in the array.\n- **`array`** (optional): The original array on which **`reduce()`** was called.\n\nThe **`callback`** function should return the accumulated value after processing the current element. The **`reduce()`** function will then iterate through the array, calling the **`callback`** function for each element, and accumulating the result. The final value returned by **`reduce()`** will be the accumulated value after processing the last element in the array.\n\nIf an **`initialValue`** is provided, it will be used as the initial accumulated value, and the **`callback`** function will start processing the first element in the array. If no **`initialValue`** is provided, the first element in the array will be used as the initial accumulated value, and the **`callback`** function will start processing the second element in the array.\n\nHere's an example of using **`reduce()`** to calculate the sum of an array of numbers:\n\n```jsx\nconst numbers = [1, 2, 3, 4, 5];\n\nconst sum = numbers.reduce((accumulator, currentValue) =\u003e {\n  return accumulator + currentValue;\n}, 0);\n\nconsole.log(sum); // 15\n```\n\nIn this example, the **`reduce()`** function is called on the **`numbers`** array, and a callback function is passed to it that takes the accumulated value and the current element, and returns the sum of the two. The **`reduce()`** function then iterates through the array, accumulating the sum of all elements, and returns the final sum.\n\nThe **`reduce()`** function is a powerful tool for performing complex operations on arrays, including calculating averages, finding maximum or minimum values, and much more. It requires a good understanding of the callback function and how it accumulates values, but can be very useful in many scenarios.\n\nThe declarative paradigm is a programming paradigm that focuses on describing what a program should achieve, rather than how to achieve it. In the declarative paradigm, a program is expressed in terms of a set of rules or constraints that define the desired output or behavior.\n\nIn a declarative program, the programmer specifies a set of rules or constraints that define the problem domain, and a set of operations or transformations that should be applied to the data in order to produce the desired output. The program does not specify the sequence of operations, but rather the relationships between data elements and the desired outcomes.\n\nOne of the key benefits of the declarative paradigm is that it allows the programmer to focus on the problem domain and the desired outcomes, rather than the details of how to achieve those outcomes. This can make programs easier to read, maintain, and reason about, especially as programs become more complex.\n\nHere's an example of a declarative program that calculates the sum of an array of numbers:\n\n```jsx\nconst numbers = [1, 2, 3, 4, 5];\n\nconst sum = numbers.reduce((accumulator, currentValue) =\u003e accumulator + currentValue, 0);\n\nconsole.log(sum); // 15\n```\n\nIn this example, the program expresses the desired outcome in terms of a single operation, the **`reduce()`** function, which takes an array of numbers and returns their sum. The program does not specify the sequence of operations that should be used to achieve this outcome, but rather relies on the built-in **`reduce()`** function to do the work.\n\nThe declarative paradigm is widely used in functional programming, where programs are expressed in terms of data transformations and operations on immutable data structures. It is also used in logic programming, where programs are expressed in terms of logical rules and constraints that define the relationships between data elements.\n\nOne of the main challenges of the declarative paradigm is that it can be less intuitive for programmers who are used to thinking in terms of imperative programs. Declarative programs can also be less efficient than imperative programs, as they may require more computation to achieve the same result. However, the benefits of declarative programming, such as increased readability and maintainability, often outweigh these drawbacks in many contexts.\n\nBelow is the implementation of `reduce`:\n\n```jsx\nArray.prototype.myReduce = function(callback, initialValue) {\n  let accumulator = initialValue === undefined ? undefined : initialValue;\n\n  for (let i = 0; i \u003c this.length; i++) {\n    if (accumulator !== undefined) {\n      accumulator = callback(accumulator, this[i], i, this);\n    } else {\n      accumulator = this[i];\n    }\n  }\n\n  return accumulator;\n}\n```\n## Promise\n\nPromise is an object that represents the eventual completion or failure of an asynchronous operation, and its resulting value.\n\nPromises are a way to handle asynchronous operations without using callbacks, which can lead to callback hell and make code difficult to read and maintain. Instead of relying on callbacks to handle the result of an asynchronous operation, you can use Promises to handle success and failure cases in a more structured way.\n\nHere's an example of how you can create a Promise in JavaScript:\n\n```jsx\nconst myPromise = new Promise((resolve, reject) =\u003e {\n  // Perform some asynchronous operation\n  setTimeout(() =\u003e {\n    const randomNumber = Math.random();\n    if (randomNumber \u003c 0.5) {\n      resolve(randomNumber); // Promise is fulfilled\n    } else {\n      reject(new Error('Number too high')); // Promise is rejected\n    }\n  }, 1000);\n});\n```\n\nIn this example, we create a new Promise object that takes a function with two arguments: **`resolve`** and **`reject`**. Within this function, we perform an asynchronous operation that resolves or rejects the Promise based on a random number. If the number is less than 0.5, the Promise is resolved with the number as its value. If the number is greater than or equal to 0.5, the Promise is rejected with an Error object.\n\nOnce we have created the Promise, we can use its methods **`then`** and **`catch`** to handle the success and failure cases respectively. Here's an example:\n\n```jsx\nmyPromise.then((result) =\u003e {\n  console.log(`The number is ${result}`);\n}).catch((error) =\u003e {\n  console.error(error);\n});\n```\n\nIn this example, we use the **`then`** method to handle the resolved Promise, and log the result to the console. We use the **`catch`** method to handle the rejected Promise, and log the error to the console.\n\nPromises can be chained together using the **`then`** method, which returns a new Promise. This allows you to create a series of asynchronous operations that depend on each other. Here's an example:\n\n```jsx\nconst myPromise = new Promise((resolve, reject) =\u003e {\n  setTimeout(() =\u003e {\n    resolve(5);\n  }, 1000);\n});\n\nmyPromise.then((result) =\u003e {\n  return result * 2; // Returns a new Promise with a value of 10\n}).then((result) =\u003e {\n  console.log(result); // Output: 10\n}).catch((error) =\u003e {\n  console.error(error);\n});\n```\n\nIn this example, we create a Promise that resolves with a value of 5 after 1 second. We then chain two **`then`** methods together to perform some calculations on the result of the Promise. The first **`then`** method multiplies the result by 2, which returns a new Promise with a value of 10. The second **`then`** method logs the final result to the console. If any errors occur, they will be caught by the **`catch`** method.\n## Async/Await\n\n**`Async/await`** is a way to handle asynchronous operations that is built on top of **`Promises`**. The main difference between **`async/await`** and **`then/catch`** is the syntax and readability.\n\nWith **`async/await`**, you write asynchronous code that looks more like synchronous code. You use the **`async`** keyword before a function declaration to indicate that it will return a promise, and the **`await`** keyword within the function body to wait for a promise to resolve before continuing execution. Here is an example:\n\n```jsx\nasync function fetchData() {\n  const response = await fetch('https://api.example.com/data');\n  const data = await response.json();\n  return data;\n}\n```\n\nIn this example, the **`fetchData`** function is declared as **`async`**, which means that it will return a **`Promise`**. Within the function, we use the **`await`** keyword to wait for the **`fetch`** method to return a response, and then we use it again to wait for the **`json`** method to parse the response into data.\n\nWith **`then/catch`**, you chain together methods that return promises using the **`then`** method. Here is an example:\n\n```jsx\nfunction fetchData() {\n  return fetch('https://api.example.com/data')\n    .then(response =\u003e response.json())\n    .catch(error =\u003e console.log(error));\n}\n```\n\nIn this example, the **`fetchData`** function returns a promise that resolves with the parsed JSON data or rejects with an error. We chain together the **`then`** and **`catch`** methods to handle the resolution and rejection of the promise.\n\nOne advantage of **`async/await`** over **`then/catch`** is that it allows for more readable and concise code, especially when dealing with complex asynchronous logic. However, it's important to note that **`async/await`** is simply a syntax for working with **`Promises`** and is not a replacement for them.\n## OOP\n\nJavaScript supports Object-Oriented Programming (OOP) through the use of objects and prototypes.\n\nIn JavaScript, an object is a collection of key-value pairs, where the key is a string (or symbol) and the value can be any data type, including other objects. You can create an object using object literal syntax, like this:\n\n```jsx\nconst person = {\n  name: 'John',\n  age: 30,\n  sayHello() {\n    console.log(`Hello, my name is ${this.name}.`);\n  }\n};\n```\n\nIn this example, we define an object called **`person`** with properties for **`name`** and **`age`**, as well as a method called **`sayHello`** that uses the **`this`** keyword to refer to the current object.\n\nIn JavaScript, you can also create object templates using constructor functions, which allow you to create multiple instances of an object with shared properties and methods. Here is an example:\n\n```jsx\nfunction Person(name, age) {\n  this.name = name;\n  this.age = age;\n}\n\nPerson.prototype.sayHello = function() {\n  console.log(`Hello, my name is ${this.name}.`);\n}\n\nconst person1 = new Person('John', 30);\nconst person2 = new Person('Jane', 25);\n```\n\nIn this example, we define a constructor function called **`Person`** that takes in **`name`** and **`age`** arguments and sets them as properties on the **`this`** object. We also define a **`sayHello`** method on the **`Person.prototype`** object, which allows all instances of **`Person`** to share the same method.\n\nTo create an instance of **`Person`**, we use the **`new`** keyword and call the **`Person`** constructor function with the desired arguments, like **`person1`** and **`person2`** in the example.\n\nJavaScript also supports inheritance through the use of prototypes. You can create a prototype chain by setting the **`prototype`** property of a constructor function to an object that contains shared properties and methods, like this:\n\n```jsx\nfunction Animal(name) {\n  this.name = name;\n}\n\nAnimal.prototype.sayHello = function() {\n  console.log(`Hello, my name is ${this.name}.`);\n}\n\nfunction Dog(name, breed) {\n  Animal.call(this, name);\n  this.breed = breed;\n}\n\nDog.prototype = Object.create(Animal.prototype);\nDog.prototype.constructor = Dog;\n\nDog.prototype.sayBark = function() {\n  console.log('Woof!');\n}\n\nconst dog1 = new Dog('Fido', 'Golden Retriever');\n```\n\nIn this example, we define a **`Dog`** constructor function that inherits from the **`Animal`** constructor function. We use **`Object.create`** to create a new object with the **`Animal.prototype`** object as its prototype, which allows **`Dog`** to inherit the **`sayHello`** method. We also define a new method called **`sayBark`** on the **`Dog.prototype`** object.\n\nTo create an instance of **`Dog`**, we use the **`new`** keyword and call the **`Dog`** constructor function with the desired arguments, like **`dog1`** in the example.\n\nOverall, JavaScript supports OOP through the use of objects, prototypes, and constructor functions, allowing you to create reusable code with shared properties and methods.\n# Fundamentals\n\nIn this session, we'll cover the basics of functional programming, which is a programming paradigm that emphasizes using functions to solve problems. We'll discuss immutability, higher-order functions, and function composition. We'll also provide practical examples in JavaScript to help you write cleaner and more maintainable code.\n\n## Pure Function\n\nA pure function is a function where the return value is determined ONLY by its input values, with no observable side effects.\n\n```jsx\n// This is Impure Function\nconst PI = 3.14\n\nfunction areaCircle(radius) {\n\treturn radius * radius * PI\n}\n\nareaCircle(10)\n\n// This is Pure Function\nfunction areaCirclePure(radius, pi) {\n\treturn radius * radius * pi\n}\n\nareaCirclePure(10, Math.PI)\n```\n\nExample 1:\n\n```jsx\n// This is pure function or impure function?\nfunction generateRandomNumber(min, max) {\n\tconst factor = max - min + 1;\n\treturn parseInt(Math.random() * factor) + min\n}\n\ngenerateRandomNumber(1,100)\n\n// The answer is: Impure Function.\n```\n\nBecause `Math.random()` is not an input value in the function.\n\nExample 2:\n\n```jsx\n// Pure\nfunction sum(a,b) {\n\treturn a + b\n}\n\nsum(60,40)\n\nlet count = 0\n\n// Impure \nfunction sumImpure(a,b) {\n\tcount++\n\treturn a + b\n}\n```\n\nBecause a side effect occurred in the function.\n## Higher Order Function\n\nFunctions that operate on other functions, taking them as arguments or returning them, are called higher-order functions.\n\n```jsx\nfunction execute(fn, ...params) {\n\treturn function(text) {\n\t\treturn `${text} ${fn(...params)}`\n  }\n}\n\nfunction sum(a,b,c) {\n\treturn a + b + c\n}\n\nfunction multiple(a,b) {\n\treturn a * b\n}\n\nexecute(sum, 4, 5, 6)('The result of sum is')\nexecute(multiple, 10, 20)('The result of multiple is')\n\n```\n## First Class Function\n\nA programming language is said to have first-class functions when functions in that language are treated like any other variable.\n\n```jsx\nconst x = 3\nconst y = function(txt) {\n\treturn `This is a text: ${txt}`\n}\n\ny('Hi')\n```\n## Immutability\n\nImmutability in functional programming refers to the concept that once a value is created, it cannot be changed. Instead, any operation that appears to modify the value actually creates a new value based on the original. This approach ensures that values are always predictable and can be safely shared and reused throughout a program.\n\n```jsx\n// This is not immutability because .sort() modified initial array\nconst nums = [3, 1, 7, 9, 4, 6]\nnums.sort()\n\nconsole.log(nums)\n\n// This is a example the immutability because create a new array\n\nconst nums = [3, 1, 7, 9, 4, 6]\n\nfunction order(array) {\n\treturn [...array].sort()\n}\n\nconsole.log(order(nums))\n```\n## Closure\n\nThis is when a function “remembers” its lexical scope, even when the function runs outside that lexical scope.\n\nExample of a closure in JavaScript:\n\n```jsx\nfunction outerFunction() {\n  const outerVariable = 'I am in the outer function!';\n\n  function innerFunction() {\n    console.log(outerVariable);\n  }\n\n  return innerFunction;\n}\n\nconst innerFunc = outerFunction();\ninnerFunc(); // Output: \"I am in the outer function!\"\n\n```\n\nIn this example, **`outerFunction`** is defined and returns the **`innerFunction`**. When we call **`outerFunction`**, it creates a variable **`outerVariable`** and defines **`innerFunction`**, which logs the value of **`outerVariable`** to the console. We then return **`innerFunction`** and store it in the **`innerFunc`** variable. When we call **`innerFunc`**, it still has access to **`outerVariable`** through the closure, even though **`outerFunction`** has completed execution.\n## Currying\n\nThat involves transforming a function that takes multiple arguments into a series of functions that each take a single argument. The returned function(s) then execute when all the arguments have been provided.\n\nFor example:\n\n```jsx\nfunction add(x) {\n  return function(y) {\n    return x + y;\n  }\n}\n\nconst add2 = add(2); // Returns a function that adds 2 to a given number\nconsole.log(add2(3)); // Output: 5\n```\n\nIn this example, **`add`** is a function that takes a single argument **`x`** and returns another function that takes a single argument **`y`**. When **`add`** is called with **`2`**, it returns a new function that adds **`2`** to any given number. We then call **`add2`** with **`3`**, which returns the sum **`5`**.\n\nCurrying is a useful technique in functional programming, as it allows us to create more modular and reusable code by breaking down complex functions into simpler ones that can be composed together.\n## Lazy Evaluation\n\nLazy evaluation is a programming technique where the evaluation of an expression is delayed until its value is actually needed. In other words, lazy evaluation means that an expression is not evaluated until the result is actually needed or requested by the program.\n\nIn languages that use lazy evaluation, expressions are only evaluated when they are actually needed. This can lead to better performance and memory usage, as the program does not waste time or resources evaluating expressions that are never used.\n\nFor example, in JavaScript, using lazy evaluation can be achieved with the use of **`\u0026\u0026`** and **`||`** operators. In the following code, **`someFunction`** is only called if **`myArray`** is not empty:\n\n```jsx\nconst myArray = [1, 2, 3];\n\n// Using the \u0026\u0026 operator for lazy evaluation\nmyArray.length \u0026\u0026 someFunction(myArray);\n```\n\nIn this example, **`someFunction`** is only called if **`myArray`** has a **`length`** greater than **`0`**. If **`myArray`** is empty, **`someFunction`** is not called at all, since the **`\u0026\u0026`** operator only evaluates the second expression if the first one is **`true`**. This is an example of lazy evaluation in JavaScript.\n## Composition\n\nFunction composition is a functional programming technique in which two or more functions are combined to form a new function. The output of the first function is passed as input to the second function, and so on. The result of the final function is the output of the composition of the functions.\n\n```jsx\nconst add = x =\u003e x + 2;\nconst multiply = x =\u003e x * 3;\nconst subtract = x =\u003e x - 1;\n\nconst composedFunction = (x) =\u003e subtract(multiply(add(x)));\n\nconsole.log(composedFunction(5)); // Output: 20\n```\n\nIn this example, we have three simple functions that perform mathematical operations on an input value **`x`**. We then combine these functions using function composition to create a new function called **`composedFunction`**. This function takes an input value **`x`**, applies the **`add`**, **`multiply`**, and **`subtract`** functions in sequence, and returns the final result. When we call **`composedFunction(5)`**, the output is **`20`**, which is the result of the following sequence of operations: **`add(5)`** returns **`7`**, **`multiply(7)`** returns **`21`**, and **`subtract(21)`** returns **`20`**.\n## Functors\n\nIn functional programming, functors are objects or data structures that can be mapped over with a higher-order function, such as **`map`**. They are a way of abstracting and encapsulating common operations on different types of data.\n\nA functor is defined by two properties: a value and a mapping function. The mapping function takes a function as input, applies it to the value inside the functor, and returns a new functor with the transformed value.\n\nHere's an example of a functor in JavaScript using an array as the data structure:\n\n```jsx\nconst myFunctor = [1, 2, 3];\n\nconst addOne = (num) =\u003e num + 1;\n\nconst mappedFunctor = myFunctor.map(addOne);\n\nconsole.log(mappedFunctor); // Output: [2, 3, 4]\n```\n\nIn this example, we create an array **`myFunctor`** with three elements. We then define a function **`addOne`** that takes a number as input and returns the input value incremented by 1. We use the **`map`** function, which is a higher-order function, to apply **`addOne`** to each element in the array. The **`map`** function returns a new functor, which is an array in this case, with the transformed values. Finally, we log the new array to the console.\n# Patterns\n\n---\n\n## Observer\n\nThe Observer pattern is a design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any changes to its state. The main idea behind the Observer pattern is to decouple the subject from its observers, allowing for greater flexibility and reusability of code.\n\nIn this pattern, the subject maintains a list of observers and provides a way for new observers to register and existing observers to unregister. When the subject's state changes, it notifies all its observers by calling a method on each one. This allows the observers to update themselves based on the new state of the subject.\n\nThe Observer pattern is widely used in GUI frameworks, event-driven architectures, and other systems where changes to an object's state need to be propagated to other parts of the system.\nIn JavaScript, the Observer pattern can be implemented using callbacks, events, or the Observable class introduced in ECMAScript 2015. For instance, we can create an observable object and add one or more observers to it using the Observable class as follows:\n\n```jsx\nclass MyObservable {\n  constructor() {\n    this.observers = [];\n  }\n\n  subscribe(observer) {\n    this.observers.push(observer);\n  }\n\n  unsubscribe(observer) {\n    this.observers = this.observers.filter(obs =\u003e obs !== observer);\n  }\n\n  notify(data) {\n    this.observers.forEach(observer =\u003e observer(data));\n  }\n}\n\nconst myObservable = new MyObservable();\n\nconst myObserver = data =\u003e {\n  console.log('Data received:', data);\n};\n\nmyObservable.subscribe(myObserver);\nmyObservable.notify('Hello World!');\n```\n\nIn this example, we define a class **`MyObservable`** that has three methods: **`subscribe`**, **`unsubscribe`**, and **`notify`**. The **`subscribe`** method adds a new observer to the list of observers, **`unsubscribe`** removes an observer from the list, and **`notify`** calls all the observers with the provided data.\n\nWe then create an instance of **`MyObservable`** called **`myObservable`** and add an observer called **`myObserver`** using the **`subscribe`** method. Finally, we call the **`notify`** method with some data, and the observer receives it and logs it to the console.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Framonxm%2Ffunctional-programming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Framonxm%2Ffunctional-programming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Framonxm%2Ffunctional-programming/lists"}