repetitive code when i try functional programming

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

>-)
Posts: 509
Joined: Tue Apr 24, 2012 1:10 am UTC

repetitive code when i try functional programming

Postby >-) » Fri Jun 13, 2014 2:23 pm UTC

i'm learning haskell, but i find that in some cases, my code devolves into a lot of repetition.

usually i'd write something like

Code: Select all

def foo(a, b, c, y)
   x = a**b mod c
   if x == y or (x*y mod 2) == 0 or x == 1
      goo(x+1, x-1)
   else
      goo(x+2, x-2)


but in haskell, i can't declare x as a variable (or at least i don't think that's proper functional programming?) so i have to substitute everything in the code, which makes for a lot of repetition.

Code: Select all

foo a b c y = if (a**b mod c) == y or ((a**b mod c)*y mod 2) == 0 or (a**b mod c) == 1
               then goo((a**b mod c)+1,(a**b mod c)-1)
            else
               goo((a**b mod c)+2,(a**b mod c)-2)


i thought breaking it up might help

Code: Select all

hoo a b c y = if (a**b mod c) == y or ((a**b mod c)*y mod 2) == 0 or (a**b mod c) == 1
            then True
         else
            False

foo a b c y = if hoo a b c y
               then goo((a**b mod c)+1,(a**b mod c)-1)
            else
               goo((a**b mod c)+2,(a**b mod c)-2)


but that only moves the repetition into another function

what's the right way to do this?

EvanED
Posts: 4324
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: repetitive code when i try functional programming

Postby EvanED » Fri Jun 13, 2014 2:35 pm UTC

>-) wrote:but in haskell, i can't declare x as a variable (or at least i don't think that's proper functional programming?)
You can, and it's totally fine!

Every FP language I've ever seen provides something like this:

Code: Select all

foo a b c y = let x = a**b mod c
              in if x == y or (x*y mod 2) == 0 or x == 1
                   then goo(x+1,x-1)
                  else
                   goo(x+2,x-2)


Haskell also provides this:

Code: Select all

foo a b c y = if x == y or (x*y mod 2) == 0 or x == 1
                 then goo(x+1,x-1)
               else
                 goo(x+2,x-2)
             where x = a**b mod c

This is fine because you're not changing x, so no side effects. It's no more "not FP" than defining a function or a global variable. (In fact, you could see that as sort of syntactic sugar for

Code: Select all

foo a b c y = foo_helper a b c y (a**b mod c)
foo_helper a b c y x = if x == y or (x*y mod 2) == 0 or x == 1
                         then goo(x+1,x-1)
                        else
                          goo(x+2,x-2)


I'm probably getting indention wrong; I don't really know Haskell.

See here for more examples, and correct-er syntax.

>-)
Posts: 509
Joined: Tue Apr 24, 2012 1:10 am UTC

Re: repetitive code when i try functional programming

Postby >-) » Fri Jun 13, 2014 2:56 pm UTC

Oh, I didn't know about where. that does make a lot of sense. thank you!

EvanED
Posts: 4324
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Re: repetitive code when i try functional programming

Postby EvanED » Fri Jun 13, 2014 3:21 pm UTC

You're welcome. Just to be clear, Haskell provides both let and where. To a large extent they seem to be personal preference between which you like better, but the link I gave does give some tradeoffs between them and they're not completely interchangable. It sounds like you're early enough that they may mostly not matter at this point, but maybe keep it in mind for the future.

User avatar
headprogrammingczar
Posts: 3072
Joined: Mon Oct 22, 2007 5:28 pm UTC
Location: Beaming you up

Re: repetitive code when i try functional programming

Postby headprogrammingczar » Mon Jun 16, 2014 9:51 pm UTC

To expand a bit further, even once you get more familiar with Haskell the decision between let and where is going to be "which do I feel like using today". They have differences, but I found that even while learning it's fairly second-nature to decide which will be more appropriate.

You should definitely get used to naming local values. It will make some scary cool stuff in the future easier to comprehend.
<quintopia> You're not crazy. you're the goddamn headprogrammingspock!
<Weeks> You're the goddamn headprogrammingspock!
<Cheese> I love you

User avatar
WarDaft
Posts: 1583
Joined: Thu Jul 30, 2009 3:16 pm UTC

Re: repetitive code when i try functional programming

Postby WarDaft » Tue Jun 24, 2014 11:55 am UTC

Personally, I use let for in-line and where for basically everything else, so I'd write:

Code: Select all

foo a b c y = if p then r1 else r2 where
    x = a**b `mod` c
    r1 = goo (x+1) (x-1)
    r2 = goo (x+2) (x-2)
At least, that's what looks most appealing to my sensibilities, and it matches the class, instance, and GADT declaration styles. Technically it matches the module style as well, if you have a fairly simple set of export conditions, but it usually doesn't look much at all like it.
All Shadow priest spells that deal Fire damage now appear green.
Big freaky cereal boxes of death.

RedNifre
Posts: 17
Joined: Mon Nov 17, 2008 1:10 am UTC

Re: repetitive code when i try functional programming

Postby RedNifre » Fri Oct 02, 2015 12:26 am UTC

This question got me thinking about how much you could actually reduce the code duplication in op's example. I wonder if this is the minimum:

Code: Select all

let foo a b c y = let
   stuff = (round (a**b)) `mod` c
   (>$<) = (<$>) . (flip ($)) ; infixr 4 >$< -- like <$> but inverted
   goo' [u,v] = goo u v
in
   goo' ((if or $ stuff >$< zipWith (.) ((==) <$> [0,y,1]) ((`mod` 2) . (* y) : repeat id) then 1 else 2) >$< stuff >$< [(+),(-)])


This works by constructing the list of parameters for the goo function out of a list of compare-to values, a list of preprocessing before comparing and a list of combining functions. The >$< operator which maps a value over a list of functions is needed to have no "($ value1) <$> ($ value2) <$> functions" where "($ x)" would be duplicated code.

Easier to understand long form:

Code: Select all

let foo a b c y = let
   stuff = (round (a**b)) `mod` c
   compareTo = [0,y,1]
   comparisons = (==) <$> compareTo
   preprocessors = (`mod` 2) . (* y) : repeat id
   conditions = zipWith (.) comparisons preprocessors
   (>$<) = (<$>) . (flip ($)) ; infixr 4 >$< -- like <$> but backwards: map value over list of functions
   check = or $ stuff >$< conditions
   number = if check then 1 else 2
   signs = [(+),(-)]
   params = number >$< stuff >$< signs
   goo' [u,v] = goo u v
in
   goo' params


... though it's obvious that having a bit of code duplication inside the functions makes it a lot more readable.

lalop
Posts: 210
Joined: Mon May 23, 2011 5:29 pm UTC

Re: repetitive code when i try functional programming

Postby lalop » Wed Nov 18, 2015 2:03 pm UTC

Like EvanED's last example, but as a single expression:

Code: Select all

foo a b c y =
  (\ x ->
    if x == y || (x*y `mod` 2) == 0 || x == 1
       then goo (x+1) (x-1)
       else goo (x+2) (x-2)
  ) (a^b `mod` c)


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 15 guests