Search code examples
haskellfiltermonads

Haskell Cartesian Product, Monad with filter


This has to be dead simple and I'm unhappy I can't figure it out at this point in my Haskell experience. I want a cartesian product of a list with itself, but I want to filter out identical items. I don't want a post filter.

This gets me the CP - seemingly set up to simply add a filter...

p as bs = do
            a <- as
            b <- bs
            return (a,b)

p [1,2,3] [1,2,3]
[(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)]

I have read that return () is basically a noop in do notation - but this doesn't compile. (Are the tuples being mixed up)

pf as bs = do
            a <- as
            b <- bs
            if a == b then return () else return (a,b)

* Couldn't match type `()' with `(a, a)'
      Expected type: [(a, a)]
        Actual type: [()]

I have tried a few other things, like the if' function from the Haskell wiki. I also tried when without success. When the filter is when

when (a /= b) return (a,b)

* Couldn't match type `m0 (a, a)' with `()'
      Expected type: (a, a) -> ()
        Actual type: (a, a) -> m0 (a, a)

I suppose these error messages are leading me by the nose to the issue but I am not adept at translating most of them yet.

There is very possibly a higher level function that might handle this in a more straight forward way (filterM ?), and I'll be happy to hear about its usage, but I still want to know how to solve this issue in the pf function above.

Thanks


Solution

  • Try:

    main = print $ p [1,2,3] [1,2,3] -- [(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)]
    
    p as bs =
      do
        a <- as
        b <- bs
        if a == b then [] else return (a, b)
    

    I've learned Haskell a bit only recently, so I may be wrong. In this context, return (a, b) is nothing more than the expression [(a, b)] (which is natural, since for monad [] you need a function with the output of type [t]). So you need to provide the same type, i.e., the empty list [].

    On the other hand when you write return () it actually is [()], whose type is [()], which is not the same as, say, the type [(Int, Int)].