Haskell: How to create a function that does not allow any, two or two applications?

Control.Applicative.optional allows you to process zero or one application. many and some of them allow 0 or more, or 1 or more, respectively. I would like to create a function that processes zero, one or two in particular. The signature may be the same as for many / some, i.e.

zeroOneOrTwo :: Alternative f => fa -> f [a] 

I feel that it should be pretty simple, but I play with him for a while and can't make it work. Any pointers would be greatly appreciated.

+6
source share
2 answers

How about this:

 zeroOneOrTwo :: Alternative f => fa -> f [a] zeroOneOrTwo a = go (2 :: Int) where go n | n > 0 = ((:) <$> a <*> go (n - 1)) <|> pure [] | otherwise = pure [] 
+8
source

If you are faced with the problem of limiting such a result, you can make its type reflect this.

 data ZOT a = Zero | One a | Two aa form :: a -> Maybe a -> ZOT a form a Nothing = One a form a (Just b) = Two ab zeroOneOrTwo :: Alternative f => fa -> f (ZOT a) zeroOneOrTwo a = (form <$> a <*> optional a) <|> pure Zero 

What if you want up to three? Or until four? You can cover all such cases at once with several language extensions.

 {-# LANGUAGE DataKinds, GADTs #-} data Nat = Z | S Nat data Natty n where Zy :: Natty 'Z Sy :: Natty n -> Natty ( n) data AtMost na where Nil :: AtMost na Cons :: a -> AtMost na -> AtMost ( n) a atMost :: Alternative f => Natty n -> fa -> f (AtMost na) atMost Zy _ = pure Nil atMost (Sy n) a = (Cons <$> a <*> atMost na) <|> pure Nil 

What if you do not want to use any fancy extensions? Well, it will not look so pretty, but you can still do it if you want by taking the page from Ralph Hinze "Numerical representations as nested data types of higher orders".

 data Z a = Z deriving (Show) data S fa = Nil | Cons a (fa) deriving (Show) class AtMost g where atMost :: Alternative f => fa -> f (ga) instance AtMost Z where atMost _ = pure Z instance AtMost g => (AtMost (S g)) where atMost m = (Cons <$> m <*> atMost m) <|> pure Nil 

Note that there are now two different ways to construct an empty result, Z and Nil , with different types. Z used when the result is as large as the query, while Nil used when it ends short.

 *AtMost> atMost (Just 3) :: Maybe ((S (S (SZ))) Int) Just (Cons 3 (Cons 3 (Cons 3 Z))) *AtMost> atMost Nothing :: Maybe ((S (S (SZ))) Int) Just Nil *AtMost> atMost undefined :: Maybe (Z Int) Just Z 
+5
source

All Articles