Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Struggling with using pure functional programming to solve an everyday problem

I saw this post in hacker news today. I am struggling with the same problems of understanding how pure functional programming will help me abstract a real world problem. I made the switch from imperative to OO programming 7 years ago. I feel that I have mastered it, and it has served me well. In the last couple years I have learned some tricks and concepts in functional programming like map and reduce, and I like them as well. I have used them in my OO code, and have been happy with that, but when abstracting a set of instructions, I can only think of OO abstractions to make the code prettier.

Recently I have been working on a problem in python, and I have been trying to avoid using OO to solve it. For the most part my solution looks imperative, and I know that I could make it look nice and clean if I used OO. I thought I would post the problem, and maybe the functional experts can pose a solution that's beautiful and functional. I can post my ugly code if I must, but would rather not. :) Here's the problem:

User can request an image or a thumbnail of the image. If the user requests the thumbnail of the image, and it doesn't yet exist, create it using python's PIL module. Also create a symbolic link to the original or thumbnail with a human readable path, because the original image name is a hashcode, and not descriptive of it's contents. Finally, redirect to the symbolic link of that image.

In OO I would likely create a SymlinkImage base class, a ThumbnailSymlinkImage subclass, and an OriginalSymlinkImage subclass. The shared data (in SymlinkImage class) will be things like the path to the original. The shared behavior will be creating the symbolic link. The subclasses will implement a method called something like 'generate' that will be responsible for creating the thumbnail if applicable, and making the call to their superclass to create the new symbolic link.

like image 200
Frank Henard Avatar asked May 31 '11 16:05

Frank Henard


People also ask

How difficult is functional programming?

Yes, functional programming tends to be difficult for many people to comprehend (I'd tend to say, especially those who've already been exposed to procedural programming first). I'd also say your example of functional programming isn't really a very good example of functional programming though.

What is the purest functional programming language?

By this definition, Haskell, Mercury, Clean etc are pure functional languages; whereas Scala, Clojure, F#, OCaml etc are impure ones.

Why is Haskell so difficult?

It's still possible to write bad Haskell, but the quality of code you can write with Haskell can never be achieved with Javascript. The code size is also much smaller with Haskell. That's because of it's powerful abstraction mechanisms. It was these mechanisms that made our learning so difficult.


2 Answers

Yeah, you'd really do this very differently using a functional approach.

Here's a sketch using the typed, by-default pure, functional programming language Haskell. We create new types for the key concepts of your problem, and break apart the work into discrete functions that do one task at a time. The IO and other side effects (like creating a symlink) are restricted to certain functions, and indicated with a type. To distinguish the two modes of operation, we use a sum type.

-- -- User can request an image or a thumbnail of the image. -- If the user requests the thumbnail of the image, and it doesn't yet exist, create it using -- python's PIL module. Also create a symbolic link to the original or -- thumbnail with a human readable path, because the original image name is a -- hashcode, and not descriptive of it's contents. Finally, redirect to the -- symbolic link of that image. --  module ImageEvent where  import System.FilePath import System.Posix.Files  -- Request types data ImgRequest = Thumb ImgName | Full ImgName  -- Hash of image  type ImgName = String  -- Type of redirects data Redirect  request :: ImgRequest -> IO Redirect request (Thumb img) = do     f <- createThumbnail img     let f' = normalizePath f     createSymbolicLink f f'     return (urlOf f)  request (Full img)  = do     createSymbolicLink f f'     return (urlOf f)     where         f  = lookupPath img         f' = normalizePath f 

Along with some helpers, which I'll leave the definition up to you.

-- Creates a thumbnail for a given image at a path, returns new filepath createThumbnail :: ImgName -> IO FilePath createThumbnail f = undefined     where         p = lookupPath f  -- Create absolute path from image hash lookupPath :: ImgName -> FilePath lookupPath f = "/path/to/img" </> f <.> "png"  -- Given an image, construct a redirect to that image url urlOf :: FilePath -> Redirect urlOf = undefined  -- Compute human-readable path from has normalizePath :: FilePath -> FilePath normalizePath = undefined 

A truly beautiful solution would abstract out the request/response model with a data structure to represent the sequence of commands to be executed. A request comes in, builds a structure purely to represent what work it needs done, and that is handed to the execution engine with does things like creating files and so on. Then the core logic will be entirely pure functions (not that there's much core logic in this problem). For an example of this style of truly purely functional programming with effects, I recommend Wouter Swiestra's paper, ``Beauty in the Beast: A Functional Semantics for the Awkward Squad''

like image 158
Don Stewart Avatar answered Oct 03 '22 08:10

Don Stewart


Personally, I think the problem is that you are trying to use Functional Programming to solve problems that are designed/stated for Imperative programming. The 3 popular paradigms (Functional, Imperative, Object-oriented) have different strengths:

  • Functional Programming emphasizes on description of WHAT to be done, usually in term of input/result.
  • Imperative Programming emphasizes on HOW to do something, usually in term of list and order of steps to taken, and states to modify.
  • Object-oriented Programming emphasizes on the RELATIONSHIPS between entities in a system

Thus, when you approach a problem, the first order of business is to rephrase it such that the intended paradigm can properly solve it. By the way, as a side node, there is no such thing as "pure OOP" as far as I know. Code in the methods of your OOP classes (be it Java, C#, C++, Python, or Objective C) are all Imperative.

Back to your example: the way that you state your problem (first, then, also, finally) is imperative in nature. As such, construction of a functional solution is almost impossible (without doing the tricks like side-effect or monads, that is). Similarly, even if you create a bunch of classes, those classes are useless in and of themselves. To use them, you have to write imperative code (albeit these codes are embedded within classes) that solve the problem step by step.

To restate the problem:

  • Input: Image type (Full or thumbnail), Image name, file system
  • Output: the requested image, the file system with the requested image

From the new problem statement, you can solve it like this:

def requestImage(type, name, fs) :      if type == "full" :         return lookupImage(name, fs), fs     else:         thumb = lookupThumb(name, fs)         if(thumb) :             return thumb, fs         else:             thumb = createThumbnail(lookupImage(name, fs))             return thumb, addThumbnailToFs(fs, name, thumb) 

Of course, this is incomplete, but we can always recursively solve lookupImage, lookupThumb, createThumbnail, and addThumbnailToFs roughly the same way.

Big Note: creating a new filesystem sounds big, but it should not be. For example, if this is a module in a bigger web server, the "new filesystem" can be as simple as the instruction to where the new thumbnail should be. Or, at the very worst, it can be an IO monad to put the thumbnail to the appropriate location.

like image 29
magice Avatar answered Oct 03 '22 08:10

magice