Avoid incomplete pattern matching

Consider the following code:

data A data B f :: A -> B f = undefined data T = TA A | TB B data ListT = ListTA [A] | ListTB [B] g :: [T] -> ListT gl = let f' :: T -> B f' (TA x) = fx f' (TB x) = x isA :: T -> Bool isA TA{} = True isA TB{} = False in case (all isA l) of True -> ListTA (map (\(TA x) -> x) l) False -> ListTB (map f' l) main = pure () 

The idea behind this is that I have a list of A or B mixed together. I can convert A -> B , but not vice versa. Based on this list, I want to make either list A or list B s, the first if all my original elements are A s, the last if at least one of them is B

The above code is compiling (and I assume this will work), but the incomplete pattern matching in map (\(TA x) -> x) l makes me a little uncomfortable. Is such an incomplete match just a necessity of what I'm doing here? Also, I am reinventing the wheel, is there something that generalizes what I am doing here?

+8
list pattern-matching haskell refactoring partial
source share
3 answers

After some help from the other answers, I'm going to answer my question for the benefit of future viewers. I believe the most suspicious function for g is as follows (and noticed that I am generalized instead of Traversable instead of just lists).

 data ListT t = ListTA (t A) | ListTB (t B) g :: (Traversable t) => t T -> ListT t gl = let f2B :: T -> B f2B (TA x) = fx f2B (TB x) = x f2A :: T -> Maybe A f2A (TA x) = Just x f2A (TB x) = Nothing in maybe (ListTB (fmap f2B l)) ListTA (traverse f2A l) main = pure () 

In lists, this should occupy a space proportional to the number of leading A s, which, in my opinion, is minimal.

+1
source share

The only way I can think of is something like

 tryA :: [T] -> Maybe [A] tryA [] = [] tryA (t:ts) = case t of TA x -> do xs <- tryA ts; return (x:xs) TB _ -> Nothing 

If tryA nothing, execute map f' l as before.

Thus, you do all isA l and map in one pass, and this avoids an incomplete template.

+2
source share

I would structure it like this: create two lists — one full A and one full B — so that creating list A might fail. You can build a Monoid that implements this logic and foldMap into it.

Since it is impossible to create a list of A s, we will need to build this Monoid on top of Maybe . The behavior that we want comes from the Maybe Applicative instance: if any of the mappend arguments is Nothing , then all this fails, otherwise we want to use mappend to combine the two results. This is a common recipe for combining Applicative and a Monoid . In particular:

 newtype WrappedApplicative fa = Wrap { unWrap :: fa } instance (Applicative f, Monoid m) => Monoid (WrappedApplicative fm) where mempty = pure mempty Wrap x `mappend` Wrap y = Wrap $ liftA2 mappend xy 

I don't know if this is newtype somewhere in base . It looked like it would be there, but I could not find it.

Without further ado, here Monoid we will foldMap ping in:

 type Result = ([B], WrappedApplicative Maybe [A]) 

I borrow a (a, b) Monoid instance that delegates parallel A and B Monoid instances.

 getAsOrToBs :: [Either AB] -> Either [A] [B] getAsOrToBs = fromResult . foldMap toResult where toResult (Left a) = ([aToB a], Wrap (Just [a])) toResult (Right b) = ([b], Wrap Nothing) fromResult (_, Wrap (Just as)) = Left as fromResult (bs, Wrap Nothing) = Right bs 

Alternatively, with foldr :

 getAsOrToBs :: [Either AB] -> Either [A] [B] getAsOrToBs = fromResult . foldr f ([], Just []) where f (Left a) (bs, mas) = (aToB a : bs, fmap (a:) mas) f (Right b) (bs, _) = (b:bs, Nothing) fromResult (_, Just as) = Left as fromResult (bs, Nothing) = Right bs 

Look ma no partial functions!

+2
source share

All Articles