Написание правильного пользовательского экземпляра для чтения

Привет товарищи Хаскеллерс,

Я изучаю Haskell с одного месяца и изо всех сил пытаюсь создать пользовательский экземпляр для чтения для личного типа данных.

Я следовал этому и соответствующей главе в Learn Yourself a Haskell, вот мой фрагмент кода.

data Position      =  Position (Absc,Ordn) deriving (Show)
instance Read (Position) where
readsPrec _ input =
    let absc = List.filter (/='(') $ takeWhile (/=',')
        ordn = List.filter (/=')') $ tail (dropWhile (/=',') )
    in (\str -> Position ( (read (absc str) :: Int)
                       , (read (ordn str) :: Int) ) ) input
type Absc = Int
type Ordn = Int

Моя цель - разобрать вход "(1,3)" вывести что-то вроде Position (1,3)

Я, однако, получаю следующие сообщения об ошибках:

• Couldn't match expected type ‘[Char]’
              with actual type ‘[Char] -> [Char]’
• Probable cause: ‘takeWhile’ is applied to too few arguments
  In the second argument of ‘($)’, namely ‘takeWhile (/= ',')’
  In the expression: filter (/= '(') $ takeWhile (/= ',')
  In an equation for ‘absc’:
      absc = filter (/= '(') $ takeWhile (/= ',')

То же самое для функции ordn.

• Couldn't match expected type ‘[(Position, String)]’
              with actual type ‘Position’
• In the expression:
    (\ str
       -> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
      input
  In the expression:
    let
      absc = filter (/= '(') $ takeWhile (/= ',')
      ordn = filter (/= ')') $ tail (dropWhile (/= ','))
    in
      (\ str
         -> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
        input
  In an equation for ‘readsPrec’:
      readsPrec _ input
        = let
            absc = filter (/= '(') $ takeWhile (/= ',')
            ordn = filter (/= ')') $ tail (dropWhile (/= ','))
          in
            (\ str
               -> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
              input

Кажется, что мой let заявление не признает absc а также ordn как функции (или, по крайней мере, попробуйте применить их напрямую, в то время как я хочу определить их как частично примененные функции, чтобы применить их позже в параметре str). Я также, вероятно, испортил Position конструктор значений.

Я не знаком со стилем кодирования на Haskell, и, возможно, я использовал некоторые ключевые слова и инструменты, которые мне не совсем понятны. Не могли бы вы намекнуть, как написать это, чтобы это работало?

Заранее спасибо.

2 ответа

Решение

В вашем коде есть куча ошибок - я думаю, вы должны немного прочитать о разнице между $ а также . это важно знать при обработке функций, большинство ваших ошибок связано с попыткой применить функцию к ее аргументу ($) вместо объединения функций (.),

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

Исправление кода

instance Read Position where
  readsPrec _ input =
      let absc = filter (/='(') . takeWhile (/=',')
          ordn = filter (/=')') . tail . dropWhile (/=',')
       in [(Position ( read (absc input) :: Int
                     , read (ordn input) :: Int), input)]

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

убирать

instance Read Position where
  readsPrec _ str = let ('(':absc,',':ordn') = break (==',') str
                        (ordn,')':str') = break (==')') ordn'
                     in [(Position (read absc, read ordn), str')]

намного лучше, мы понимаем, что в начале и в конце должны быть скобки, но все же немного громоздко.

используя существующий функционал

Знаю это Tupleэто уже примеры Read

instance Read Position where
  readsPrec s str = [(Position x, rest) | (x,rest) <- readsPrec s str]

хорошо, но все же мы можем сделать лучше

Скажем, мы недавно немного поработали с кортежами и нашли очень удобный модуль Data.Bifunctor это имеет функции first а также second преобразовать первый и второй компонент 2-кортежа (любой бифунктор на самом деле).

Мы можем упростить вышеупомянутое

instance Read Position where
  readsPrec s = map (first Position) . readsPrec s

чистый и короткий.

С помощью show "(3,4)"::Position возвращается Position (3,4), Большое спасибо за ваш подробный ответ. Я склонен путать $ а также . обозначения, но после того, как я просмотрел документацию, стало ясно.

Другие вопросы по тегам