Uroc327
Uroc327

Reputation: 1429

Pattern matching with Alternative empty or Applicative pure

I know it is possible, to pattern match against (named) constructors like so:

f1 :: Maybe a -> Bool
f1 Nothing = False
f1 (Just x) = True -- in reality have something that uses x here

f2 :: [a] -> Int
f2 [] = False
f2 x = True

How can I write such a function for general Alternatives similar to

f :: (Alternative m) => m a -> Bool
f empty = False
f x = True

If I try this, I get the error Parse error in pattern: empty. Which makes sense, I guess, as empty as a function here and not a constructor. But how can I accomplish this for general Alternatives idiomatically?

Edit 1: My actual goal is to define a Monad instance (and probably also a MonadPlus instance) for a custom result type. Instead of the basic Either Error Result type, it should support a Maybe Error (and if possible also other Alternatives like [Error]) as error type and also some Applicative as result type in order to support lazy evaluation, for example with the result type (Maybe Error, [Tokens]) of a tokenizer.

I'd like something similar to

instance (Alterantive mErr, Applicative mRes) => Monad (mErr e, mRes a) where
  return x = (empty, pure x)
  (empty, pure x) >>= f = f x
  (err, x) >>= f = (err, x)

Upvotes: 4

Views: 365

Answers (3)

duplode
duplode

Reputation: 34398

Slipping in an Eq constraint, as in mithrandi's answer, is indeed the best that can be done. In any case, it is worth emphasising that it comes at a cost: we are now stuck with an Eq constant that would be unnecessary were we merely pattern match against, say, [] or Nothing. A common way to avoid this problem is using null :: Foldable t => t a -> Bool. Here, however, that option is not great either, as Foldable is largely unrelated to Alternative and rather alien to your use case. In particular, there is no guarantee that, in the general case, there will be just one value for which null holds, which means it might conceivably hold for values that aren't the empty of the relevant Alternative instance.

Ultimately, then, the only tool that would fully fit the requirements might well be some Alternative subclass with an isEmpty method. I don't think that exists anywhere, and the power-to-weight ratio doesn't seem encouraging when it comes to conjuring such a thing.

Upvotes: 1

mingmingrr
mingmingrr

Reputation: 146

It is in fact possible using -XPatternSynonyms and -XViewPatterns:

{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
import Control.Applicative (empty)

pattern Empty :: (Eq (m a), Alternative m) => m a
pattern Empty <- ((==) empty -> True)

f :: (Eq (m a), Alternative m) => m a -> Bool
f Empty = False
f _ = True

Upvotes: 2

mithrandi
mithrandi

Reputation: 1650

The best you can do is:

f :: (Eq (m a), Alternative m) => m a -> Bool
f x | x == empty = False
    | otherwise = True

Upvotes: 5

Related Questions