APLC Notes
APLC Notes
Contents
Programming Paradigms.............................................................................2
Purity and HOF............................................................................................5
Currying and Partial Applications................................................................7
Lambda.....................................................................................................10
Immutability and Nullability......................................................................11
Recursion:..................................................................................................14
Logic Programming and Prolog:.................................................................15
Evaluation Strategy:..................................................................................17
Programming paradigm
An approach to programming based on a mathematical theory
Each concept is best suited for certain kind of problem
OOP best for problems with high no. of data abstractions.
Logic programming best for navigating complex code
Each paradigm consists of different structures, features, and opinions
Solving a programming problem requires the correct solution = concepts
Javascript is a multi paradigm model.
What is purity?
o A crucial concept in functional programming style that provides the ability
for the functions to avoid side effects and remain deterministic. It enables the
possibility of parallel processing of functions without worrying of the possibilities
of side effects.
o When working with a pure function
Obtain the same results every time you call the function
Reverse the order of calls to different functions
Process the function calls in parallel without any consequence
Evaluate the function calls in any order, assuming that the entire
language does not allow side effects
Remove the function if no other functions rely on its output
- Second side effect: 'fip()' modifies external code by mutating the state of 'y'.
Lambda
o A function that can be created without belonging to any class.
o Also called arrow function in javascript
o Can be passed around as an object or execute on demand
Example
Reduce function
o Reducing an array in a single value
o Iteration returns an accumulated value based on the previous result
o Value is kept until the end of the array is reached
o Result is always a single value
o Example : To get sum of lengths of the words in animals array as number
Currying
o The process of taking a function that accepts n
arguments and turning it into n functions that each accepts
a single argument .
Arity
o It represents The numbers of arguments a function
takes.
o Unary = 1 parameter
o Binary = 2 parameters
o Ternary = 3 parameters
o n-nary = takes n number of arguments
o Nullary = 0 parameters
Currying transforms a n-nary function into n unary
function.
Partial application
o The process of fixing several arguments to a function,
producing another function of smaller arity .
Full application
o Applying all arguments of a function
o Applying fewer arguments than the total is a partial
application
Lambda
Lambda Calculus
o A framework To study the interaction of functional abstraction and function
application from an abstract, purely mathematical perspective
o Functional abstraction begins by breaking a particular problem into a series
of steps
o Function application is the act of applying the function to an argument and
obtaining value as output
o Lambda calculus uses only functions
o Lambda calculus has no state or side effects
o Order of evaluation is irrelevant
o Functions are unary, receiving one argument
A Lambda expression is a short function that uses arrow-function
o It has No name is assigned to it
Functions vs Methods
A function is a piece of code that can be called by its name
Can pass arguments and return values optimally
A method is a piece of code that is called and associated with an object
Lambda parameters
Expressions are effectively just functions
Can take parameters
o Zero
o One
o Multiple
Example :
Callback functions
Computer programs may not always run sequentially.
o But code can run asynchronously.
Callbacks make sure that a function is not going to run before a task is completed.
o Keep the code safe from problems and errors.
o It aids in developing asynchronous code.
Example
Closures
o A data structure that binds a function to its environment
when it’s declared
A static or lexical scope surrounding the function
definition
o Allows you to create functions that can remember and
modify variables from their outer scope
o Useful for handling private data and encapsulation
o Allow you to associate data with a function that operates
on that data
Memoization
o An optimization technique used primarily to speed up
computer programs
o By storing the results of expensive function calls to pure
functions
o Returning cached result when the same inputs occur
again
o Also known as tabling
Nullability
o Ability of variables to have an absence of value
Example:
o Variable not initialized
o Function failed to construct objects or any computation
o Component is not required or disposed
o Often nullability is represented using null, nil, none etc
o Handle nullability
Null-check : Use annotations like
@NonNull,@Nullable, or isNull in lodash,js, Optional in
Java
Example :
Immutability Concepts
Immutable Classes
o Design your custom classes to be immutable by:
Making fields final (so they cannot be modified
after initialization).
Not providing setter methods.
Returning new instances instead of modifying
existing ones.
Immutable Collections
o Use immutable versions of collections (e.g., List.of,
Set.of, Map.of) provided by Java.
o These collections cannot be modified after creation.
o Example: List<String> names = List.of("Alice", "Bob",
"Charlie");
String Immutability
o Strings in Java are immutable by design.
o Any operation on a string creates a new string instance.
o Avoid using += or concat for string concatenation;
prefer StringBuilder.
Defensive Copying
o When returning a mutable object from a method, return
a defensive copy to prevent modification.
o Example
public List<Integer> getNumbers() {
return List.copyOf(numbers); // Defensive copy
}
Thread Safety with Immutability
o Immutable objects are inherently thread safe.
o Use them for shared data structures to avoid
synchronization issues.
Recursion:
A technique for solving problems by breaking them down into
smaller, self-similar instances of the same problem.
Must have a base case (stop condition) and a recursive case
(where it calls itself with a smaller input).
Is essential in pure functional programming languages that
lack looping constructs.
Recursion vs. Iteration:
Recursion
o Involves a function calling itself
o It can be more concise and readable but may consume
more memory due to the function call stack.
Iteration
o Uses loops.
o More memory-efficient, but recursive solutions can be
more elegant for certain problems.
Any recursive problem can be solved iteratively, but not all
problems have a natural recursive solution.
Head Recursion and Tail Recursion:
Head recursion occurs when the recursive call is not the last
operation in the function.
o Check for recursive condition first
o Base case is evaluated last
Tail recursion happens when the recursive call is the last
operation performed by the function.
o Base case is evaluated first
o If base case is not fulfilled, then proceed to recursion
call
Tail recursion can be optimized by some
compilers/interpreters to use a constant amount of stack space,
making it more memory efficient.
Functional Programming and Recursion:
Recursion is a fundamental technique in functional
programming, where side effects are avoided.
Functional programming focuses on operations rather than
data structures, promoting code that is easier to reason about.
Recursion helps achieve this by breaking down problems into
smaller, self-similar instances, removing local side effects.
Benefits of Recursion:
Can lead to more concise and readable code for certain types
of problems.
o Readability and Maintainability
Promotes a functional programming style by avoiding side
effects and mutable states.
o Predictability
Encourages breaking down complex problems into simpler,
self-similar sub-problems.
o Abstraction & Encapsulation
Additional Context:
Recursion has a wide range of applications
o Traversing data structures (e.g., trees, linked lists)
o Solving mathematical problems (e.g., factorial, Fibonacci
series)
o Implementing algorithms (e.g., merge sort, quicksort).
While recursion can be elegant and powerful, it's essential to
ensure that the base case is correctly defined to avoid infinite
recursion, which can lead to stack overflow errors.
Functional programming languages often favor recursion over
iteration due to their focus on immutable data and avoiding side
effects.
In summary, recursion is a fundamental technique in functional
programming that promotes breaking down complex problems into
simpler, self-similar instances, leading to more concise and readable
code while avoiding local side effects. However, it's important to
consider its memory implications and ensure the correct base case
is defined to prevent infinite recursion.
Backtracking:
Prolog uses backtracking to find all possible solutions to a
query by exploring alternative variable bindings.
o Like solving a puzzle by testing different pieces until
they fit together perfectly
If a set of variable bindings fails to satisfy a goal, Prolog
backtracks and tries a different set of bindings.
Data Structures and Pattern Matching:
Prolog supports compound terms (structures) as arguments to
predicates, allowing for complex data representations.
Pattern matching is used to match queries against facts and
rules, with variables, constants, and structures following specific
matching rules.
Logical Negation and Closed-World Assumption:
Prolog uses negation as failure, where a proposition is
considered false if it cannot be proven from the given facts and
rules.
This is based on the closed-world assumption, where Prolog's
knowledge is limited to the provided facts and rules.
Applications of Prolog:
Prolog is well-suited for tasks like databases, expert systems,
theorem proving, and natural language processing.
Its declarative nature and backtracking capabilities make it
useful for solving problems that involve logical reasoning and
symbolic manipulation.
Additional Context:
Prolog differs from imperative programming languages in its
approach to variables, machine model assumptions, and
execution model.
While Prolog has its strengths, it may not be suitable for all
types of problems, especially those requiring extensive numerical
computations or high-performance requirements.
Prolog has various implementations with different syntax and
semantics, such as SWI Prolog, which has an active user
community.
Lists in Prolog:
Lists are a fundamental data structure in Prolog, represented
using square brackets and commas to separate elements.
Lists can contain constants, variables, structures, and even
sublists.
The empty list is represented as [].
Head and Tail of Lists:
Prolog provides a mechanism to separate the first element
(head) from the rest of the list (tail) using the | operator.
This allows for pattern matching and recursive operations on
lists.
Pattern Matching with Lists:
Pattern matching is a powerful feature in Prolog for working
with lists.
Variables can match any element, and the anonymous
variable _ matches any single element.
Structures can match other structures with the same functor
and matching arguments.
Recursive Rules for Lists:
Many list operations in Prolog are defined using recursive
rules.
The base case typically handles the empty list, while the
recursive case operates on the head and tail of the list.
Examples include the last predicate to find the last element of
a list, and the member predicate to check if an element is a
member of a list.
List Predicates:
Prolog provides built-in predicates for common list operations,
such as member, append, and reverse.
o Member
Evaluation Strategy:
An evaluation strategy defines the rules for evaluating
expressions in a programming language.
Concept: parameter-passing strategy which defines what kind
of value is passed to a function for each parameter, sometimes
called binding strategy, and then evaluated.
For example, executing a function call f(a,b) first evaluates
and stores the arguments a and b. The result is at the reference
or memory locations ref_a and ref_b and the body of the function
is evaluated using the passed references – call-by-reference
evaluation
It specifies the order in which expressions are evaluated and
how arguments are passed to functions.
Evaluation Order:
Evaluation order determines the sequence in which sub-
expressions are evaluated within an expression.
Different languages may have different evaluation orders
(e.g., left-to-right, right-to-left, or unspecified).
Example :
o JS ,Java and C # programs have left to right evaluation
order
o OCaml has right to left evaluation order
o C++ have unspecified order
Strict vs. Non-Strict Evaluation:
Strict evaluation (eager /greedy evaluation) means that
function arguments are fully evaluated before the function is
applied.
o Uses call-by-value and call-by-reference
Non-strict evaluation (lazy evaluation) means that arguments
are not evaluated until their values are needed within the
function.
Haskell is a well-known non-strict language, while most
mainstream languages like JavaScript, Java, and C++ use strict
evaluation.
Call By Reference:
Also known as call-by-address or pass-by-reference, pass-by-reference evaluation
strategy allows the caller to pass an expression that is evaluated to a reference
instead of a value. This means that the argument contains a reference pointing to
the memory location of the passed value.
No Copies: It avoids copying values, instead passing references, which modifies
variables directly.
Implicit References: Parameters are bound to implicit references, modifying
variables used as arguments.
Difficulty in Tracing Effects: This strategy makes it harder to trace function call
effects, leading to subtle bugs.
Call By Value