![Declarative Programming]()
Programming paradigms influence the way developers think about and write code. Two of the most essential styles are imperative and declarative programming. Gaining clarity on these paradigms and knowing when to use each can greatly improve your efficiency and coding approach. In this article, we’ll explore the key differences between imperative and declarative programming, using practical examples in C# to showcase the declarative style in action, and highlight features such as LINQ.
Imperative Programming
Imperative programming is a programming style where the developer writes code that describes how a task should be performed, step by step. It focuses on changing the program’s state through statements like loops, conditionals, and variable assignments. In C#, examples include using `for` loops or `if-else` blocks to control the flow of execution. This approach gives you full control over the logic and state changes.
Key Features of Imperative Programming
- Control Flow: The programmer explicitly defines the flow of execution using loops (for, while), conditionals (if-else), and function calls.
- State Changes: Relies on mutable variables and step-by-step updates to manage state throughout the program.
- Language Support: Commonly used in languages like C, C++, and also supported in C# through traditional control structures.
Imperative Programming Example with C# Code
Let’s consider a scenario where we have a list of students, each with a set of subject-wise marks. Our goal is to calculate the total marks for each student. Using imperative programming, we’ll write step-by-step instructions to loop through the data, accumulate the marks, and print the result for each student.
public class ImperativeExample
{
static void Main(string[] args)
{
List<Student> students = new List<Student>();
students.Add(new Student()
{
StudentId = 1,
StudentName = "Student 1",
studentMarks = new List<StudentMarks>()
{
new StudentMarks() { SubjectId = 1, Mark = 30 },
new StudentMarks() { SubjectId = 2, Mark = 50 },
new StudentMarks() { SubjectId = 3, Mark = 70 },
new StudentMarks() { SubjectId = 4, Mark = 65 },
new StudentMarks() { SubjectId = 5, Mark = 80 }
}
});
students.Add(new Student()
{
StudentId = 2,
StudentName = "Student 2",
studentMarks = new List<StudentMarks>()
{
new StudentMarks() { SubjectId = 1, Mark = 70 },
new StudentMarks() { SubjectId = 2, Mark = 90 },
new StudentMarks() { SubjectId = 3, Mark = 70 },
new StudentMarks() { SubjectId = 4, Mark = 95 },
new StudentMarks() { SubjectId = 5, Mark = 80 }
}
});
foreach (var item in students)
{
int total = 0;
for (int i = 0; i < item.studentMarks.Count; i++)
{
total += item.studentMarks[i].Mark;
}
Console.WriteLine($"Student: {item.StudentName} || Total Marks: {total}");
}
Console.ReadKey();
}
}
Output
![Output]()
Declarative Programming
Declarative programming is a style where the developer describes the desired outcome, rather than detailing the exact steps to achieve it. It focuses on expressions and logic, leaving control flow to the language or framework. In C#, declarative code often utilizes features like LINQ, which enables data transformation or querying in a concise and readable manner. This approach promotes cleaner and more maintainable code.
Key Features of Declarative Programming
- High-Level Constructs: Focuses on writing what the program should accomplish using expressions and declarations, rather than explicit step-by-step instructions.
- Abstracted Control Flow: The control flow is managed by the language or framework, not the programmer.
- Common Languages/Technologies: Widely seen in SQL, HTML, CSS, and functional programming languages like Haskell. In C#, LINQ is a strong example of a declarative approach.
Declarative Programming Example with C# Code
Now, let’s achieve the same goal by calculating total marks for each student using a declarative approach. Instead of manually looping and updating totals, we’ll use LINQ in C# to express what we want: the sum of each student’s marks. The underlying logic, like iteration and aggregation, is handled by the framework, resulting in cleaner and more concise code.
public class DeclarativeExample
{
static void Main(string[] args)
{
List<Student> students = new List<Student>
{
new Student
{
StudentId = 1,
StudentName = "Student 1",
studentMarks = new List<StudentMarks>
{
new StudentMarks { SubjectId = 1, Mark = 30 },
new StudentMarks { SubjectId = 2, Mark = 50 },
new StudentMarks { SubjectId = 3, Mark = 70 },
new StudentMarks { SubjectId = 4, Mark = 65 },
new StudentMarks { SubjectId = 5, Mark = 80 }
}
},
new Student
{
StudentId = 2,
StudentName = "Student 2",
studentMarks = new List<StudentMarks>
{
new StudentMarks { SubjectId = 1, Mark = 70 },
new StudentMarks { SubjectId = 2, Mark = 90 },
new StudentMarks { SubjectId = 3, Mark = 70 },
new StudentMarks { SubjectId = 4, Mark = 95 },
new StudentMarks { SubjectId = 5, Mark = 80 }
}
}
};
var studentTotals = students.Select(s => new
{
s.StudentName,
TotalMarks = s.studentMarks.Sum(m => m.Mark)
});
foreach (var student in studentTotals)
{
Console.WriteLine($"Student: {student.StudentName} || Total Marks: {student.TotalMarks}");
}
Console.ReadKey();
}
}
Output
![Console App]()
In the example discussed, imperative programming gives full control over logic, allowing us to add if-else conditions, such as checking if a student’s mark is below 35 and applying grace marks to help them pass. In contrast, declarative programming (like LINQ in C#) focuses on what to compute, making it harder to embed such conditional logic inline, especially when decisions depend on step-by-step state changes.
Imperative Example with Grace Mark Logic
public class ImperativeExample
{
static void Main(string[] args)
{
List<Student> students = new List<Student>();
students.Add(new Student()
{
StudentId = 1,
StudentName = "Student 1",
studentMarks = new List<StudentMarks>()
{
new StudentMarks() { SubjectId = 1, Mark = 30 },
new StudentMarks() { SubjectId = 2, Mark = 50 },
new StudentMarks() { SubjectId = 3, Mark = 70 },
new StudentMarks() { SubjectId = 4, Mark = 65 },
new StudentMarks() { SubjectId = 5, Mark = 80 }
}
});
students.Add(new Student()
{
StudentId = 2,
StudentName = "Student 2",
studentMarks = new List<StudentMarks>()
{
new StudentMarks() { SubjectId = 1, Mark = 70 },
new StudentMarks() { SubjectId = 2, Mark = 90 },
new StudentMarks() { SubjectId = 3, Mark = 70 },
new StudentMarks() { SubjectId = 4, Mark = 95 },
new StudentMarks() { SubjectId = 5, Mark = 80 }
}
});
foreach (var student in students)
{
int total = 0;
foreach (var mark in student.studentMarks)
{
int actualMark = mark.Mark;
// Apply grace marks if mark is below 35
if (actualMark < 35)
{
Console.WriteLine($"Subject {mark.SubjectId}: Grace applied to {actualMark}");
actualMark += 5; // Adding grace marks
}
total += actualMark;
}
Console.WriteLine($"Student: {student.StudentName} || Total Marks (with grace): {total}");
}
Console.ReadKey();
}
}
Output
![Debug]()
Imperative vs. Declarative Programming – Comparison Table
Aspect |
Imperative Programming |
Declarative Programming |
Approach to Problem Solving |
Describes how to perform tasks step by step |
Describes what result is needed without detailing steps |
Handling Execution Flow |
Control flow is manually defined using loops and conditionals |
Control flow is handled by the language or framework |
Way of Managing Data and State |
Relies on mutable variables and explicit state changes |
Prefers immutability and abstracts state management |
Level of Code Abstraction |
Lower-level, closer to how the computer processes instructions |
Higher-level, focused on logic and desired outcomes |
Conclusion
Both imperative and declarative programming paradigms offer unique strengths, and understanding when to use each can significantly improve the clarity, efficiency, and maintainability of your code. Declarative approaches—such as using LINQ in C#—allow you to focus on the what rather than the how, reducing boilerplate and improving readability.
By exploring and practicing both paradigms, you'll become a more adaptable and effective developer. Choose the style that best fits your problem, and let your code speak clearly.