How do you call functions dynamically with Haskell

Go To StackoverFlow.com

7

I'm looking to call functions dynamically based on the contents found in an association list.

Here is an example in semi-pseudo-code. listOfFunctions would be passed to callFunctions.

listOfFunctions = [('function one', 'value one')
                   , ('function two', 'value two')
                   , ('function three', 'value three')]

callFunctions x = loop through functions
                  if entry found 
                  then call function with value
                  else do nothing

The crux of the question is not looping through the list, rather, it's how to call a function once I have it's name?

Consider this use case for further clarification. You open the command prompt and are presented with the following menu.

1: Write new vHost file

2: Exit

You write the new vHost file and are not presented with a new menu

1: Enter new directive

2: Write file

3: Exit

You enter some new directives for the vHost and are now ready to write the file.

The program isn't going to blindly write each and every directive it can, rather, it will only write the ones that you supplied. This is where the association list comes in. Writing a giant if/then/else or case statement is madness. It would be much more elegant to loop through the list, look for which directives were added and call the functions to write them accordingly.

Hence, loop, find a function name, call said function with supplied value.

Thanks to anyone who can help out with this.

Edit:

Here is the solution that I've come up with (constructive critiques are always welcome).

I exported the functions which write the directives in an association list as every answer provided said that just including the function is the way to go.

funcMap = [("writeServerName", writeServerName)
           ,("writeServeralias", writeServerAlias)
           ,("writeDocRoot", writeDocRoot)
           ,("writeLogLevel", writeErrorLog)
           ,("writeErrorPipe", writeErrorPipe)
           ,("writeVhostOpen", writeVhostOpen)]

In the file which actually writes the hosts, that file is imported.

I have an association list called hostInfo to simulate some dummy value that would be gathered from an end-user and a function called runFunction which uses the technique supplied by edalorzo to filter through both the lists. By matching on the keys of both lists I ensure that the right function is called with the right value.

import Vhost.Directive

hostInfo =     [("writeVhostOpen", "localhost:80")
                ,("writeServerName", "norics.com")] 

runFunctions = [f val | (mapKey, f) <- funcMap, (key, val) <- hostInfo, mapKey == key]
2012-04-04 03:12
by OpCodeOmega


8

Since I am farily new to Haskell I will risk that you consider my suggestion very naive, but anyways here it goes:

let funcs = [("sum", (+3),1),("product", (*3),2),("square", (^2),4)]
[f x | (name, f, x) <- funcs, name == "sum"]

I think it satisfies the requirements of the question, but perhaps what you intend is more sofisticated than what I can see with my yet limitted knowledge of Haskell.

2012-04-04 03:25
by Edwin Dalorzo


16

You can simply include the function in the list directly; functions are values, so you can reference them by name in a list. Once you've got them out of the list, applying them is just as simple as func value. There's no need to involve their names at all.

2012-04-04 03:17
by ehird
I know that you have more votes on your answer, however, since I ended up using edalorzo's code I accepted his answer - OpCodeOmega 2012-04-06 02:31


2

It might be a bit of an overkill (I agree with ehird's reasoning) but you can evaluate a string with Haskell code by using the eval function in System.Eval.Haskell.

EDIT

As pointed out in the comments, hint is a better option for evaluating strings with Haskell expressions. Quoting the page:

This library defines an Interpreter monad. It allows to load Haskell modules, browse them, type-check and evaluate strings with Haskell expressions and even coerce them into values. The library is thread-safe and type-safe (even the coercion of expressions to values). It is, esentially, a huge subset of the GHC API wrapped in a simpler API. Works with GHC 6.10.x and 6.8.x

2012-04-04 03:22
by Óscar López
plugins hasn't been updated since 2010, and doesn't work with recent GHCs; hint is a better option if you need to interpret Haskell code at runtime - ehird 2012-04-04 03:36
@ehird thanks! I updated my answer with your suggestion - Óscar López 2012-04-04 03:41
@ehird someone announced a few days ago that plugins is under new maintainership and a new version should be released in a few days. Although it's not out yet - John L 2012-04-04 08:04


1

First we define our list of functions. This could be built using more machinery, but for the sake of example I just make one explicit list:

listOfFunctions :: [(Int, IO ())]
listOfFunctions = [(0, print "HI")        -- notice the anonymous function
                  ,(1, someNamedFunction) -- and something more traditional here
                  ]

someNamedFunction = getChar >>= \x -> print x >> print x

Then we can select from this list however we want and execute the function:

executeFunctionWithVal :: Int -> IO ()
executeFunctionWithVal v = fromMaybe (return ()) (lookup v listOfFunctions)

and it works (if you import Data.Maybe):

Ok, modules loaded: Main.
> executeFunctionWithVal 0
"HI"
> executeFunctionWithVal 01
a'a'
'a'
2012-04-04 03:19
by Thomas M. DuBuisson
Note that, in this case, the list doesn't actually contain any functions; just IO actions - ehird 2012-04-04 03:22
Anyone care to comment on their down vote - Thomas M. DuBuisson 2012-04-04 14:55


1

Don't store the functions as strings, or rather, try storing the actual functions and then tagging them with a string. That way you can just call the function directly. Functions are first class values, so you can call the function using whatever name you assign it to.

2012-04-04 03:22
by Josh Infiesto
Ads