xuiqzy
xuiqzy

Reputation: 196

How to return a function calling another function based on the input?

I'm really quite new to Haskell and I need to return a function that "has modified" the inputted function.

I guess you cant copy and modify the original function (based on some condition) so you have to implement own behaviour directly and then call the original function?

This is my approach:

switchFirstEgg eggCarton = if eggCarton 1 == 0
                                then switchedCarton where switchedCarton position = if position == 1 then 2 else eggCarton position
                                else if eggCarton 1 == 1
                                        then switchedCarton where switchedCarton position = if position == 1 then 0 else eggCarton position
                                        else if eggCarton 1 == 2
                                                then switchedCarton where switchedCarton position = if position == 1 then 1 else eggCarton position
                                                else switchedCarton where switchedCarton position = eggCarton position

The error I get from the GHCI is

haskell/eggcartons.hs:42:54: parse error on input ‘where’

which points to the first word after the first where.

(For reference: I also tried to set more brackets here http://pastebin.com/2wTqAqpm and I tried doing it with guards http://pastebin.com/RVm28Y7n, but that's only making it worse without thouroughly understanding that? At least guards worked for me here http://pastebin.com/uQeFwLU5)

I searched for returning functions in Haskell but I only got a few random info that I used with the where things I have done.

Is my concept right? Is it only a minor mistake?

Any help on further reading regarding the syntax of returning functions is also much appreciated!

Upvotes: 1

Views: 409

Answers (2)

leftaroundabout
leftaroundabout

Reputation: 120711

First let's make this somewhat readable...

switchFirstEgg ec
     = if ec 1 == 0
        then sc where sc pos = if pos == 1
                                then 2
                                else ec pos
        else if ec 1 == 1
              then sc where sc pos = if pos == 1
                                      then 0
                                      else ec pos
              else if ec 1 == 2
                    then sc where sc pos = if position == 1
                                            then 1
                                            else ec pos
                    else sc where sc pos = ec pos

Now. where can only be used once per definition, i.e. after you wrote switchFirstEgg _ = ... you can follow a where which is valid for everything after that =. Or, you could more locally use a where after one of those definitions of sc. But you can't stick it anywhere in the middle of your code, like in an if branch.

The very similar let construct does allow this, so the easiest translation of what you attempted would be

switchFirstEgg ec
     = if ec 1 == 0
        then let sc pos = if pos == 1 then 2
                                      else ec pos
             in sc
        else if ec 1 == 1
              then let sc pos = if pos == 1 then 0
                                            else ec pos
                   in sc
              else if ec 1 == 2
                    then let sc pos = if pos == 1 then 1
                                                  else ec pos
                         in sc
                    else let sc pos = ec pos
                         in sc

but that's clunky. You don't really need to define sc with a name if it's used only once, immediately after the definition. That's a clear application for lambdas:

switchFirstEgg ec
     = if ec 1 == 0
        then \pos -> if pos == 1 then 2
                                 else ec pos
        else if ec 1 == 1
              then \pos -> if pos == 1 then 0
                                       else ec pos
              else if ec 1 == 2
                    then \pos -> if pos == 1 then 1
                                             else ec pos
                    else \pos -> ec pos

Better, but this chain of if is obviously better expressed as a single collection of case clauses.

switchFirstEgg ec = case ec 1 of
     0 -> \pos -> if pos == 1 then 2
                              else ec pos
     1 -> \pos -> if pos == 1 then 0
                              else ec pos
     2 -> \pos -> if pos == 1 then 1
                              else ec pos
     _ -> \pos -> ec pos

At this point it becomes clear that the pos bindings are pretty uniform, so we can just move them all one level up:

switchFirstEgg ec pos = case ec 1 of
     0 -> if pos == 1 then 2
                      else ec pos
     1 -> if pos == 1 then 0
                      else ec pos
     2 -> if pos == 1 then 1
                      else ec pos
     _ -> ec pos

There we have ifs immediately following pattern matches. This is now a perfect fit for guards:

switchFirstEgg ec pos = case ec 1 of
     0 | pos == 1  -> 2
     1 | pos == 1  -> 0
     2 | pos == 1  -> 1
     _ -> ec pos

Alternatively, you can just match immediately on pos, before even considering ec 1:

switchFirstEgg ec 1 = case ec 1 of
     0   -> 2
     1   -> 0
     2   -> 1
     n   -> n
switchFirstEgg ec pos = ec pos

Upvotes: 6

WolfeFan
WolfeFan

Reputation: 1447

See if this does what you want:

switchFirstEgg eggCarton = 
  switchedCarton
  where switchedCarton = case (eggCarton 1) of
                         0 -> \position -> if position == 1
                                           then 2
                                           else eggCarton position
                         1 -> \position -> if position == 1
                                           then 0
                                           else eggCarton position
                         2 -> \position -> if position == 1
                                           then 1
                                           else eggCarton position
                         _ -> \position -> eggCarton position

You can only have one where clause in a function definition (although function definitions within where clauses can have their own where clauses).

Upvotes: 3

Related Questions