Home >Web Front-end >JS Tutorial >Clean Code: Managing Side Effects with Functional Programming
Side effects can make code unpredictable and hard to maintain. Let's explore how functional programming helps us handle them effectively.
A function creates a side effect if it does anything beyond taking inputs and returning outputs. Some common side effects include:
Here's an example that shows problematic side effects:
let userProfile = { name: "Alice Johnson", email: "alice@example.com", preferences: { theme: "dark", notifications: true } }; function updateUserTheme(newTheme) { userProfile.preferences.theme = newTheme; } function toggleNotifications() { userProfile.preferences.notifications = !userProfile.preferences.notifications; } // Multiple functions modifying the same global state updateUserTheme("light"); toggleNotifications(); console.log(userProfile); // State is unpredictable
This code has several issues:
Here's an improved version using functional programming principles:
const createUserProfile = (name, email, theme, notifications) => ({ name, email, preferences: { theme, notifications } }); const updateTheme = (profile, newTheme) => ({ ...profile, preferences: { ...profile.preferences, theme: newTheme } }); const toggleNotifications = (profile) => ({ ...profile, preferences: { ...profile.preferences, notifications: !profile.preferences.notifications } }); // Usage const initialProfile = createUserProfile( "Alice Johnson", "alice@example.com", "dark", true ); const updatedProfile = updateTheme(initialProfile, "light"); const finalProfile = toggleNotifications(updatedProfile); console.log(initialProfile); // Original state unchanged console.log(finalProfile); // New state with updates
Here's how to handle necessary side effects in file operations using functional programming:
// Separate pure business logic from side effects const createUserData = (user) => ({ id: user.id, name: user.name, createdAt: new Date().toISOString() }); const createLogEntry = (error) => ({ message: error.message, timestamp: new Date().toISOString(), stack: error.stack }); // Side effect handlers (kept at the edges of the application) const writeFile = async (filename, data) => { const serializedData = JSON.stringify(data); await fs.promises.writeFile(filename, serializedData); return data; }; const appendFile = async (filename, content) => { await fs.promises.appendFile(filename, content); return content; }; // Usage with composition const saveUser = async (user) => { const userData = createUserData(user); return writeFile('users.json', userData); }; const logError = async (error) => { const logData = createLogEntry(error); return appendFile('error.log', JSON.stringify(logData) + '\n'); };
// Pure function - same input always gives same output const calculateTotal = (items) => items.reduce((sum, item) => sum + item.price, 0); // Side effect wrapped in a handler function const processPurchase = async (items) => { const total = calculateTotal(items); await saveToDatabase(total); return total; };
const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x); const processUser = pipe( validateUser, normalizeData, saveUser );
const transformData = (data) => { const addTimestamp = (item) => ({ ...item, timestamp: new Date().toISOString() }); const normalize = (item) => ({ ...item, name: item.name.toLowerCase() }); return data .map(addTimestamp) .map(normalize); };
Testing becomes much simpler with pure functions:
describe('User Profile Functions', () => { const initialProfile = createUserProfile( 'Alice', 'alice@example.com', 'dark', true ); test('updateTheme returns new profile with updated theme', () => { const newProfile = updateTheme(initialProfile, 'light'); expect(newProfile).not.toBe(initialProfile); expect(newProfile.preferences.theme).toBe('light'); expect(initialProfile.preferences.theme).toBe('dark'); }); test('toggleNotifications flips the notifications setting', () => { const newProfile = toggleNotifications(initialProfile); expect(newProfile.preferences.notifications).toBe(false); expect(initialProfile.preferences.notifications).toBe(true); }); });
Functional programming offers powerful tools for managing side effects:
These practices lead to code that's easier to test, understand, and maintain.
How do you handle side effects in your functional code? Share your approaches in the comments!
The above is the detailed content of Clean Code: Managing Side Effects with Functional Programming. For more information, please follow other related articles on the PHP Chinese website!