2

I am currently learning Haskell. I am trying to write a function which given two list of n distinct elements returns true if one is a permutation of the other. I am doing this as an exercise.

I first wrote it as :

isPermut :: (Eq a)=>[a]->[a]->Bool
isPermut u v = foldl (\acc x -> acc && (elem x u)) True v

This seems to work. I now try to rewrite it without lambda expression. So I try :

isPermut :: (Eq a)=>[a]->[a]->Bool
isPermut u v = foldl (&& (flip $ elem u)) True v

This gives me a compile error :

Couldn't match expected type `b0 -> Bool' with actual type `Bool'
Expected type: Bool -> b0 -> Bool
  Actual type: Bool -> Bool
In the first argument of `foldl', namely `(&& (flip $ elem u))'
In the expression: foldl (&& (flip $ elem u)) True v

What does this error mean? What is the correct way to write the function without lambda? Thanks.

user1188374
  • 829
  • 2
  • 7
  • 9
  • Your first function doesn't test whether one list is a permutation of the other; it tests whether all of the elements of `v` are in `u`, regardless of whether `u` contains elements not in `v` or whether `v` contains more instances of a given value than `u` does. – jwodder Feb 03 '12 at 20:32
  • 1
    Note that a simpler and more efficient way to check if a list is a permutation of another is to just sort them and check if the results are equal. (Although, this requires `Ord`, not just `Eq`). – hammar Feb 03 '12 at 20:34
  • 1
    jwodder : yes i know but both lists have the same lengths and are made of distinct elements so it boils down to the same thing. hammar : indeed, this is smarter. thanks. – user1188374 Feb 03 '12 at 20:41
  • @hammar - though that requires Ord, not just Eq. If all you have is Eq, the most efficient algorithm is probably O(n^2) - keep taking the head of one list and searching and extracting the first match from the second. If the extract fails at any point, or if the second list isn't empty at the end, you don't have a permutation. There may be an O(n) approach using a well designed hash computation (assuming you can hash the items), but that's not exact - there can be false positives for the same reason that hash tables have to deal with collisions. –  Feb 03 '12 at 20:44

2 Answers2

7

Folds have to take a function of two arguments. You gave a function of one argument since (&& whatever) is the same as \ x -> x && whatever.

To not use a lambda here, you need some way to compose &&, a two-argument function, with (`elem` u), a one-argument function. (Note that (`elem` u) is the same as (flip elem) u or \ x -> elem x u.)

Tikhon Jelvis
  • 67,485
  • 18
  • 177
  • 214
  • 1
    `pointfree` says the fold function should be written as `(. flip elem u) . (&&)` – Lily Ballard Feb 03 '12 at 20:29
  • @KevinBallard: Yeah. I meant to leave the actual answer out to let the OP figure it out himself. – Tikhon Jelvis Feb 03 '12 at 20:33
  • 2
    Fair enough, but this sort of pointfree expression can be very hard to work out by hoof if you're not already very familiar with the weird tricks (like using a `.` section). When I have to do this I usually ask `pointfree`, then decide whether or not the results are easy to understand, and if they're not then I avoid using it. It's especially helpful when trying to remember the `->` monad tricks with `ap` and `=<<` (rather, to remember which one puts the arguments in which order). – Lily Ballard Feb 03 '12 at 20:35
  • Hmm, that's a good point. And playing around with `pointfree` can be pretty instructive too. – Tikhon Jelvis Feb 03 '12 at 20:39
  • thanks! I dumbly believed that if "whatever" was a function of one argument this would make it a function of two arguments but I realizen now that's not how function composition works. – user1188374 Feb 03 '12 at 20:43
7

Let's do it one step at a time.

\acc x -> acc && (elem x u)
= { write (&&) in prefix form }
\acc x -> (&&) acc (elem x u)
= { definition of flip }
\acc x -> (&&) acc (flip elem u x)
= { definition of (.) }
\acc x -> ((&&) acc . flip elem u) x
= { eta reduction }
\acc -> (&&) acc . flip elem u
= { write (.) in prefix form }
\acc -> (.) ((&&) acc) (flip elem u)
= { definition of flip }
\acc -> flip (.) (flip elem u) ((&&) acc)
= { definition of (.) }
\acc -> (flip (.) (flip elem u) . (&&)) acc
= { eta reduction }
flip (.) (flip elem u) . (&&)
= { syntax sugar for sections }
(. flip elem u) . (&&)
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380