-1

In Haskell, is it possible to pattern match on functions that have a type variable for the outer but not inner type? Here, for example, funky takes any type constructor that takes a Bar. While this works for destructuring the two cases, I'm not sure how to write the global case on the bottom, which should ideally return the result of the destructuring. I've put a question mark before q because that's what's stumping me - I'm not sure how to destructure that last pattern match to get the Bar out. Thanks in advance for your help!

data Bar = Bar
data Foo = AA Bar | BB Bar | CC Bar | DD Bar

funky :: x Bar -> Bar
funky (AA z) = z
funky (BB z) = z
funky (? q) = q
Mike
  • 716
  • 4
  • 10
  • 5
    Can you provide more context on what your actual goal is? This looks like an [XY problem](https://meta.stackexchange.com/q/66377/386992). – Joseph Sible-Reinstate Monica Apr 18 '20 at 06:08
  • Apart from the little confusion about type constructors vs data constructors, this is a duplicate of: [Can I match a data constructor wildcard in Haskell?](https://stackoverflow.com/questions/32158110/can-i-match-a-data-constructor-wildcard-in-haskell). I also don't know why this was downvoted, since the linked question doesn't provide much more, and has 11 upvotes. – MikaelF Apr 18 '20 at 06:19
  • Also related: [Haskell/GHC: Matching multiple unary constructors with the same pattern](https://stackoverflow.com/questions/7131954/haskell-ghc-matching-multiple-unary-constructors-with-the-same-pattern). – MikaelF Apr 18 '20 at 06:39
  • @MikaelF I’m pretty sure those aren’t duplicates: those are asking about pattern matching multiple constructors of a single type at once, whereas this is asking about pattern matching on a single constructor but with a type variable. (At least, that was my interpretation of the question; I could be wrong.) – bradrn Apr 18 '20 at 07:57
  • 1
    @JosephSible-ReinstateMonica I am creating a state machine using `quickcheck-state-machine` for a legacy API that takes one argument of type `Request`. So the state machine has commands ie `data Command = Start Request | Stop Request | Query Request`. Sometimes, I want to write a function that transforms the request independent of the state. So what it looks like this means is that I either need to do one command `data Command = Command Request` or, if I use `Stop` `Start` etc, do pattern matching for every function that uses a `Command` even if the way it treats the `Request` is the same. – Mike Apr 18 '20 at 08:24
  • @Mike To solve this, couldn’t you just write a function `transform :: (Request -> Request) -> Command -> Command` to handle the pattern matching, and then just use that? You could go further with this and define `data Command' a = Start a | Stop a | Query a` and then say `type Command = Command' Request`; then `Command` becomes a functor which you can map over (so `transform` is equivalent to `fmap`). – bradrn Apr 18 '20 at 09:10
  • Great minds think alike, that is what I wound up doing :-D – Mike Apr 18 '20 at 18:40

1 Answers1

2

You can’t pattern-match on a function with a type variable on the outside. To see why, let’s look at your code:

data Bar = Bar
data Foo = AA Bar | BB Bar | CC Bar | DD Bar

funky :: x Bar -> Bar

The type signature for funky states that it takes a value of type x Bar and returns a value of type Bar. Importantly, it can do this for any type x of the appropriate kind — that’s what a type variable means.

Now, to see the problem with this, consider this type:

data Ignore a = Ignored

That is, for any type a, there is only one value of type Ignore a, and that value is Ignored.

So using this type, if we take x ~ Ignore, then funky becomes funky :: Ignore Bar -> Bar. The problem here should be obvious: there is no value of type Bar stored in Ignore Bar! So there’s no way to pattern-match on Ignore Bar and get out a Bar. And hence there is no way in general to write a function funky :: x Bar -> Bar which does what you want.

bradrn
  • 8,337
  • 2
  • 22
  • 51