Explaining functional programming to object-oriented programmers and less technical people

后端 未结 12 1451
小蘑菇
小蘑菇 2021-01-30 23:09

What are some good examples that I can use to explain functional programming?

The audience would be people with little programming experience, or people who only have ob

12条回答
  •  执念已碎
    2021-01-31 00:11

    The audience would be people with little programming experience,

    Is it really possible to explain functional programming, let along OO or procedural or any paradigm of programming to people without much programming experience?

    or people who only have object-oriented experience.

    Well probably the best examples would be converting known design patterns into their functional equivalent. Let's take the canonical example of converting a list of ints to a list of strings:

    using System;
    
    namespace Juliet
    {
        interface IConvertor
        {
            U Convert(T value);
        }
    
        class Program
        {
            static U[] Convert(T[] input, IConvertor convertor)
            {
                U[] res = new U[input.Length];
                for (int i = 0; i < input.Length; i++)
                {
                    res[i] = convertor.Convert(input[i]);
                }
                return res;
            }
    
            class SquareInt : IConvertor
            {
                public string Convert(int i)
                {
                    return (i * i).ToString();
                }
            }
    
            class ScaleInt : IConvertor
            {
                readonly int Scale;
    
                public ScaleInt(int scale)
                {
                    this.Scale = scale;
                }
    
                public string Convert(int i)
                {
                    return (i * Scale).ToString();
                }
            }
    
            static void Main(string[] args)
            {
                int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    
                string[] squared = Convert(nums, new SquareInt());
                string[] tripled = Convert(nums, new ScaleInt(3));
            }
        }
    }
    

    Its simple, readable, object-oriented, its even generic so it works for any arbitrary types, so what's the problem? For a start, its bloated: I have an interface definition and two interface implementations. What if I need another conversion? Well, I need another interface implementation -- it can get out of hand quickly.

    When you think about it, the IConvertor class is just a wrapper around a single function called Convert -- the class literally exists to help us pass Convert to other functions. If you can understand this much, then you already understand the basic principles behind functions as first-class values -- functional programming is all about passing functions to other functions in pretty much the same way you pass a person or an int or a string.

    People usually prefer functional programming because it helps them avoid single-method interfaces and implementations. Instead of passing a class, we just pass a function by name or anonymously:

    using System;
    
    namespace Juliet
    {
        class Program
        {
            static U[] Convert(T[] input, Func convertor)
            {
                U[] res = new U[input.Length];
                for (int i = 0; i < input.Length; i++)
                {
                    res[i] = convertor(input[i]);
                }
                return res;
            }
    
            static string SquareInt(int i)
            {
                return (i * i).ToString();
            }
    
            static void Main(string[] args)
            {
                int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    
                string[] squared = Convert(nums, SquareInt); // pass function by name
                string[] tripled = Convert(nums, i => (i * 3).ToString()); // or pass anonymously
            }
        }
    }
    

    Alright, so now we have the exact same program in much fewer lines of code, its readable, and its very evident what its doing.

    Now someone might say "that's a neat trick, but when would I use it" -- there are plenty of cases when you'd want to pass functions around like this. It gives you a lot more power to abstract your programs control flow in novel ways. Adapted from an example shown here, consider a class which handles files:

    class FileFunctions
    {
        internal void SaveFile()
        {
            SaveFileDialog fileDialog = new SaveFileDialog();
            fileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                File.AppendAllText(fileDialog.FileName, MyDocument.Data);
            }
        }
    
        internal void WriteFile()
        {
            OpenFileDialog fileDialog = new OpenFileDialog();
            fileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                MyDocument.Data = File.ReadAllText(fileDialog.FileName);
            }
        }
    }
    

    Yuck. The code is repetitious, but its performing operations on different classes and invoking different methods. How would you abstract away the duplicated code in the OO universe? In functional programing, it's trivial:

    class FileFunctions
    {
        internal void ShowDialogThen(FileDialog dialog, Action f)
        {
            dialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                f(dialog);
            }
        }
    
        internal void SaveFile()
        {
            ShowDialogThen(
                new SaveFileDialog(),
                dialog => File.AppendAllText(dialog.FileName, MyDocument.Data));
        }
    
        internal void WriteFile()
        {
            ShowDialogThen(
                new OpenFileDialog(),
                dialog => { MyDocument.Data = File.ReadAllText(dialog.FileName); });
        }
    }
    

    Awesome.

提交回复
热议问题