Randomize
Randomize

Reputation: 9103

How to pass a generic function as parameter to another function?

Let's say I have three functions:

data Foo = Foo { x :: String, y :: String, z:: String }

fooZero :: Foo
fooZero = Foo "empty" "empty" "empty"

fooOne :: String -> Foo
fooOne a = Foo a "empty" "empty"

fooThree :: String -> String -> String -> Foo
fooThree a b c = Foo (doSomething a) (doSomethingElse b) (doAnotherThing c)

Now I want to be able to pass them (f :: (???? -> Foo)) in another function, to be executed only in case it is required by another expensive function (evalSomething):

bar :: Int -> Int -> (???? -> Foo) -> Int
bar a b f = if (a == b) then a
            else if (a > b) then evalSomething f -- return Int
            else a + b

in this way:

let one = "One"
let two = "Two"
let three = "Three"

bar 8 8 (fooZero)
bar 1 2 (fooOne one)
bar 5 3 (fooThree one two three)

How can I do that?

Upvotes: 1

Views: 1507

Answers (2)

leftaroundabout
leftaroundabout

Reputation: 120711

As n.m. said in the comments, you simply don't need any function type at all there – the first-order type bar :: Int -> Int -> Foo -> Int will do the job just fine, since you don't actually feed the foo functions any arguments within bar – those arguments are passed before applying bar, i.e. you only pass the result. Thanks to lazyness it won't matter if the foo functions are expensive so you'd rather not evaluate them if not needed: Haskell will make sure function results aren't evaluated if you don't actually need them!

Generally speaking, there is a thing that allows you to “pass generic functions as parameters” though. It's called Rank-2 polymorphism.

{-# LANGUAGE Rank2Types    #-}
{-# LANGUAGE UnicodeSyntax #-}

bar :: Int -> Int -> (∀ t . Monoid t => t -> Foo) -> Int
bar a b f
  | a == b     = a
  | a > b      = f (mempty :: [])
  | otherwise  = a + b

(If you're not a fan of Unicode syntax, you can also write as forall.) That doesn't actually seem to be what you want for your application though.

Upvotes: 8

happydave
happydave

Reputation: 7187

Just declare it as:

bar :: Int -> Int -> Foo -> Int
bar a b f = if (a == b) then a
        else if (a > b) then evalSomething f
        else a + b

And call it as:

bar 5 3 (fooThree one two three)

Since Haskell is lazy, fooThree won't actually be evaluated unless it's needed inside evalSomething.

Upvotes: 2

Related Questions