简体   繁体   中英

Haskell pattern matching inside of case

I am trying to perform pattern matching inside of case clause:

-- different types of Parsers:
-- type Parser = String -> Maybe (String, String)
-- type Parser = String -> Maybe (Tree, String)
-- type Parser = String -> Maybe (Int, String)

-- can be generalized as:

data Parser a = Parser (String -> Maybe (a, String))

class Monad' m where
 result :: a -> m a
 bind :: m a -> (a -> m b) -> m b

instance Monad' Parser where
 result v = Parser (\input -> Just (v, input))
 bind (Parser fa) fb = Parser (\input ->
                                 case (fa input) of
                                  Nothing -> Nothing
                                  Just(v, input') -> pb input' where pb in fb v = (Parser pb)
                              )

The problem is with where pb in fb v = (Parser pb) . How do I declare pb and then pattern match with call to (fb input').

Also the need for including the constructor Parser in the declaration: data Parser a = Parser (String -> Maybe (a, String)) seems to complicate things a lot. It would have much simpler to write: data Parser a = String -> Maybe (a, String) , but that is not possible are there other ways to achieve this?

You may use let or where to create pattern matching in any place those expressions are allowed: (shortened some lines to make it fit in StackOverflow's format)

instance Monad' Parser where
 result v = Parser (\input -> Just (v, input))
 bind (Parser fa) fb = 
   Parser (\input -> case (fa input) of
       Nothing -> Nothing
       Just(v, input') -> pb input'
         where (Parser pb) = fb v
   )

= or =

instance Monad' Parser where
 result v = Parser (\input -> Just (v, input))
 bind (Parser fa) fb = 
   Parser (\input -> case (fa input) of
       Nothing -> Nothing
       Just(v, input') ->
         let (Parser pb) = fb v
          in  pb input'
   )

Haskell 98 does not allow instance declarations for type synonyms. GHC advertises a language extension that allows this, but my experience is that it lead to an ever deeper hole of problems.

The standard way to work around the need to pattern match is to create a record and use the field to strip away the constructor. The convention seems to be to use un ++ the name of the type.

data Parser a = Parser { unParser :: (String -> Maybe (a, String))}

-- N.B. unParser :: Parser a -> String -> Maybe (a, String)

instance Monad' Parser where
 result v = Parser (\input -> Just (v, input))
 bind (Parser fa) fb = 
   Parser (\input -> case (fa input) of
       Nothing -> Nothing
       Just(v, input') -> unParser (fb v) input
   )

The question you didn't ask

Did you know that Maybe is a Monad ? When you find yourself writing a lot of extra code to propagate Nothing -> Nothing it is really great to just ignore failure and let the Monad (or Applicative) instance be your friend.

instance Monad' Parser where
 result v = Parser (\input -> Just (v, input))
 bind (Parser fa) fb = 
   Parser (\input -> do -- This is in the Maybe Monad. 
                        -- If any step fails the result is Nothing
       (v, input') <- fa input
       unParser (fb v) input'
   )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM