2015-09-02 3 views
1
newtype Parser a = Parser { runParser :: String -> Maybe (a, String) } 

first :: (a -> b) -> (a, c) -> (b, c) 
first f (a, c) = (f a, c) 

inParser f = Parser . f . runParser 

-- My solution 
instance Functor Parser where 
    fmap g (Parser f) = Parser (\xs -> fmap (first g) (f xs)) 


-- Second solution 
instance Functor Parser where 
    fmap = inParser . fmap . fmap . first 

Есть два определения fmap выше, как они эквивалентны?Функциональная композиция по сравнению с noob lambdas, встречающаяся внутри экземпляра fmap functor с использованием парсеров

Можете ли вы объяснить, как работает второе решение? спросил

Тот же вопрос здесь: Implementing Parser Functor

ответ

3

Ваше решение работает с fmap экземпляром Maybe и я думаю, вы понимаете, это правильно?

Второй трудно получить, да - так, может быть, стоит вновь ввести некоторые точки и ( .. )?

Сначала давайте добавить подпись к inParser:

inParser :: ((String -> Maybe (a, String)) -> 
       (String -> Maybe(b, String))) 
      -> Parser a -> Parser b 

, как вы можете видеть это на самом деле просто разворачивает/обертывания ядро ​​проблемы;) - с помощью этого вы можете сосредоточиться на работе только с String -> Maybe (a,String)

другие части (fmap, first) есть по той же причине: они разворачивать вещи, пока вы можете получить в a действовать на него:

fmap :: (a -> b) -> Parser a -> Parser b 
fmap f p 
{ def } 
= inParser (fmap . fmap $ first f) $ p 
{ def inParser } 
= Parser . (fmap . fmap $ first f) . runParser $ p 

запомнить runParser p принимает String и возвращает Maybe (a, String)

first f является (a,c) -> (b,c) или здесь (a,String) -> (b,String) и первый fmap есть используя экземпляр Maybe -функтор, чтобы перевести это в Maybe (a,String) -> Maybe (b,String).

следующий fmap переведет String -> Maybe (a, String) в String -> Maybe (b, String), используя функтор-экземпляр (->) String.

И наконец Parser просто обертывает его снова.

Надеюсь, это немного поможет.


сказав, что ваша версия возможно более читаемым и в то время как второй один хороший мозг тизер и, кажется, более лаконична/Haskelly Я предпочел бы первый вариант;)

1

полезный способ расшифровать точечный свободный код, такие как

fmap = inParser . fmap . fmap . first 

, чтобы забыть, что код на самом деле делает, а просто посмотреть на типы.

Мы начинаем с функцией типа

fun :: a -> b 

при условии, что аргумент нашей определен fmap оперирует.Мы применяем first, изменяя его тип

first $ fun :: (a, String) -> (b, String) 

Выше, я догадался, что второй компонент был String, глядя на тип парсера. Тогда мы применяем fmap ... так как есть Maybe в определении парсера мы предполагаем, это должно быть сделано в том типе:

fmap . first $ fun :: Maybe (a, String) -> Maybe (b, String) 

Другого fmap. Теперь мы думаем, что должно быть в (->) String функтора:

fmap . fmap . first $ fun :: (String -> Maybe (a, String)) -> 
          (String -> Maybe (b, String)) 

Наконец, inParser обручи внутри newtype:

inParser . fmap . fmap . first $ fun :: Parser a -> Parser b 

Следовательно:

inParser . fmap . fmap . first :: (a -> b) -> Parser a -> Parser b 

Voila.

Обратите внимание, что нам нужно было угадать несколько вещей, посмотрев на тип анализатора. Это делает код трудным для чтения, если вы каким-то образом не знаете, каков ваш конечный тип назначения.

Для этого в этом случае я бы не рекомендовал использовать стиль с нулевой точкой.

Смежные вопросы