Search code examples
haskellmonadspurely-functionalpure-function

Using parameters that don't change after being read


I'm learning Haskell and writing a program to solve a toy problem. The program uses a parameter k that doesn't change while it is running, after the parameter has been read from a file. I'm very new to using pure functions, and I would like to write as many functions as pure ones as I can.

I have a data type Node, and functions to compare nodes, get the descendants of nodes, and more. Currently, all of these functions take a parameter k as an argument, such as

compare k node1 node2 = ...
desc k node = ...

and whenever I have to recursively call any of these within the function, I have to repeat the k parameter. It seems redundant, since k will never have a different value for these functions and since it makes the type signatures less readable, and I would like to refactor it out if possible.

Are there any strategies to do this with pure functions, or is it simply a limitation I'll have to deal with?

What I've thought of

Earlier I hard-coded k at the top level, and it seemed to work (I was able to use k in the functions without needing it as an explicit argument). But this obviously wasn't feasible once I needed to read input from file.

Another possible strategy would be to define all these functions in the main function, but this seems to be strongly discouraged in Haskell.


Solution

  • Ultimately you have to pass in the value of k everywhere it is needed, but there are some things you can do to avoid repeating it.

    One thing you can do is to define convenience functions once the value of k is known:

    myfunc = let k = ...
                 compare' = compare k
                 desc'    = desc k
             in ...
                (use compare' and desc' here)
    

    Another approach is to use the Implicit Parameters extension. This involves defining compare and desc to take k as an implicit parameter:

    {-# LANGUAGE ImplicitParameters #-}
    
    compare :: (?k :: Int) => Node -> Node
    compare n1 n2 = ... (can use ?k here) ...
    
    desc :: (?k :: Int) => Node
    desc = ... (can use ?k here) ...
    
    myfunc = let ?k = ...
             in ... use compare and desc ...
    

    Note that in either case you can't call compare or desc until you've defined what k is.