19

わかりました。Applicative型クラスに含まれるものと、それがなぜ役立つかはわかりました。しかし、重要な例でそれをどのように使用するかについて、頭を悩ませることはできません。

たとえば、次のかなり単純な Parsec パーサーを考えてみましょう。

integer :: Parser Integer
integer = do
  many1 space
  ds <- many1 digit
  return $ read ds

Monadのインスタンスを使用せずに、一体どのように記述しますParserか? 多くの人がこれを行うことができ、良いアイデアだと主張していますが、どのように正確に理解することはできません.

4

3 に答える 3

39

私は書くだろう

integer :: Parser Integer
integer = read <$ many1 space <*> many1 digit

<$>左連想 (アプリケーションのような) パーサー構築演算子, <*>, <$,がたくさんあります<*。左端のものは、コンポーネントの値から結果の値を組み立てる純粋な関数のはずです。各演算子の右側にあるものはパーサーであり、文法の構成要素を左から右にまとめて与えます。どの演算子を使用するかは、次の 2 つの選択肢によって決まります。

  the thing to the right is    signal  / noise
  _________________________            
  the thing to the left is \           
                            +-------------------
                    pure /  |   <$>       <$
                  a parser  |   <*>       <*

したがって、read :: String -> Integerパーサーのセマンティクスを提供する純粋な関数として選択すると、先頭のスペースを「ノイズ」として分類し、数字の束を「シグナル」として分類できます。したがって、

 read <$ many1 space <*> many1 digit
 (..)    (.........)     (.........)
 pure    noise parser     |
 (.................)      |
     parser              signal parser
 (.................................)
                    parser

複数の可能性を組み合わせることができます

p1 <|> ... <|> pn

不可能を表現する

empty

パーサーでコンポーネントに名前を付ける必要はめったになく、結果のコードはセマンティクスが追加された文法のようになります。

于 2013-02-27T23:05:58.430 に答える
11
integer :: Parser Integer
integer = read <$> (many1 space *> many1 digit)

または

integer = const read <$> many1 space <*> many1 digit

これらのどちらかがより読みやすいと思うかどうかは、あなた次第です。

于 2013-02-27T22:19:47.383 に答える
8

あなたの例は、Applicativeにもっとはっきりと似ている形に徐々に書き直すことができます:

do
  many1 space
  ds <- many1 digit
  return $ read ds
  1. 表記の定義do

    many1 space >> (many1 digit >>= \ds -> return $ read ds)
    
  2. の定義$

    many1 space >> (many1 digit >>= \ds -> return (read ds))
    
  3. の定義.

    many1 space >> (many1 digit >>= (return . read))
    
  4. 第3モナド法則(結合法則):

    (many1 space >> many1 digit) >>= (return . read)
    
  5. liftMdo表記以外の)の定義:

    liftM read (many1 space >> many1 digit)
    

これは、動作があなたの例と同じです(または、私が混乱していない場合はそうする必要があります:))。

ここで、を、に置き換えるliftMと、Applicativeが得られます。fmap<$>>>*>

read <$> (many1 space *> many1 digit)

、、およびは一般に同義語であると想定されているためliftM、これは有効です。fmap<$>>>*>

これはすべて機能し、元の例ではパーサーの結果を使用して次のパーサーを構築しなかったため、これを行うことができます。

于 2013-03-01T14:07:59.160 に答える