Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: How to organize a group of functions that all take the same arguments

Tags:

haskell

dry

I am writing a program with several functions that take the same arguments. Here is a somewhat contrived example for simplicity:

buildPhotoFileName time word stamp = show word ++ "-" ++ show time ++ show stamp
buildAudioFileName time word = show word ++ "-" ++ show time ++ ".mp3"
buildDirectoryName time word = show word ++ "_" ++ show time

Say I am looping over a resource from IO to get the time and word parameters at runtime. In this loop, I need to join the results of the above functions for further processing so I do this:

let photo = buildPhotoFileName time word stamp
    audio = buildAudioFileName time word
    dir   = buildDirectoryName time word
in ....

This seems like a violation of "Don't Repeat Yourself" principle. If down the road I find I would like to change word to a function taking word, I might make a new binding at the beginning of let expression like so:

let wrd   = processWord word
    photo = buildPhotoFileName time wrd stamp
    audio = buildAudioFileName time wrd
    dir   = buildDirectoryName time wrd
in ....

and would have to change each time I wrote word to wrd, leading to bugs if I remember to change some function calls, but not the others.

In OOP, I would solve this by putting the above functions in a class whose constructor would take time and word as arguments. The instantiated object would essentially be the three functions curried to time and word. If I wanted to then make sure that the functions receive processWord word instead of word as an "argument", I could call processWord in the constructor.

What is a better way to do this that would be more suited to Functional Programming and Haskell?

like image 632
DJG Avatar asked Jun 18 '13 19:06

DJG


1 Answers

Since you say you're ready to create an OO-wrapper-class just for that, I assume you're open to changing your functions. Following is a function producting a tuple of all three results you wanted:

buildFileNames time word stamp = 
  ( show word ++ "-" ++ show time ++ show stamp,
    show word ++ "-" ++ show time ++ ".mp3",
    show word ++ "_" ++ show time )

You'll be able to use it like so:

let wrd   = processWord word
    (photo, audio, dir) = buildFileNames time wrd stamp
    in ....

And if you don't need any of the results, you can just skip them like so:

let wrd   = processWord word
    (_, audio, _) = buildFileNames time wrd stamp
    in ....

It's worth noting that you don't have to worry about Haskell wasting resources on computing values you don't use, since it's lazy.

like image 51
Nikita Volkov Avatar answered Jan 02 '23 21:01

Nikita Volkov