Нужна помощь в устранении путаницы с возвращаемыми типами

У меня есть функция, слишком сложная для того, чтобы я мог указать, каким должен быть тип функции. Я пытаюсь заставить GHC согласиться с тем, что я ожидаю того же, что и он. Во-первых, функция, которую я думаю, она должна делать. Тогда, где путаница приходит.

flagScheduled ((Left (MkUFD day)):rest) = do
   match <- runDB $ selectList [TestStartDate ==. Just day,
                                TestStatus /<-. [Passed,Failed]] []
   case (L.null match) of
      True -> do
               processedDays <- ([(Left $ MkUFD day)] :) <$> flagScheduled rest
               return processedDays
      False -> do
                let flaggedRange = (calcFlagged match)
                    product      = (testFirmware . snd . P.head) match
                processedDays <- (flagScheduled'
                                  ([(Left $ MkUFD day)] ++
                                   (L.take flaggedRange) rest) (show product) :) <$>
                                   (flagScheduled . L.drop flaggedRange) rest
                return processedDays
flagScheduled ((Right day):rest) = do
     processedDays <- ((Right $ day):) <$> flagScheduled rest
     return processedDays
flagScheduled _ = return []

calcFlagged (( _ ,(Test _ _ (Just startDate) (Just endDate) _ _ )) : rest) =
  fromIntegral $ C.diffDays endDate startDate

flagScheduled' toBeFlagged product =
   L.map (flagIt product) toBeFlagged
    where flagIt product (Left (MkUFD day)) = Right $
                                              MkCal $
                                              Right $
                                              MkUAD $
                                              Left  $
                                               MkSDay day
                                                      (read product :: Product)
                                                      Reserved

Идея состоит в том, что я начинаю с [Either UnFlaggedDay CalendarDay] и перебираю список, превращая некоторые UnFlaggedDay в CalendarDay. Другие функции преобразуют остальные UnFlaggedDays. Ниже я определяю типы, с которыми я работаю.

newtype AvailableDay = MkAD (Text, C.Day)
                          deriving (Show, Eq)

newtype UnAvailableDay = MkUAD (Either ScheduledDay Out_Of_Office)
                             deriving Show

data ScheduledDay = MkSDay C.Day Product ScheduledState
                       deriving Show

newtype ReservedDay = MkRDay (C.Day,Product)
                                 deriving (Ord,Show,Eq,Read)

newtype ASAPDay = MkADay (C.Day,Product)
                            deriving (Ord,Show,Eq,Read)

newtype UnFlaggedDay = MkUFD C.Day

newtype CalendarDay = MkCal (Either AvailableDay UnAvailableDay)
                               deriving Show

Итак, вот проблема, когда я компилирую, я получаю эту ошибку, которая сама по себе не сбивает с толку.

    Utils/BuildDateList.hs:173:44:
        Couldn't match expected type `Either a0 b0'
                    with actual type `[Either UnFlaggedDay CalendarDay]'
        Expected type: GGHandler sub0 master0 monad0 [Either a0 b0]
          Actual type: GGHandler
                         sub0 master0 monad0 [[Either UnFlaggedDay CalendarDay]]
        In the return type of a call of `flagScheduled'
        In the second argument of `(<$>)', namely `flagScheduled rest'

Хорошо, хорошо, похоже, все, что мне нужно сделать, это применить удачный concat, и я могу сделать фактический тип GGHandler sub0 master0 monad0 [[Either UnFlaggedDay CalendarDay]] соответствующим ожидаемому типу GGHandler sub0 master0 monad0 [[Either UnFlaggedDay CalendarDay]]

Но подождите, не все так просто. Вот одна попытка из многих, и независимо от того, где я размещаю concat, похоже, это приводит к одной и той же ошибке.

   Utils/BuildDateList.hs:164:16:
       Couldn't match expected type `[Either UnFlaggedDay b0]'
                   with actual type `Either UnFlaggedDay b0'
       Expected type: GGHandler
                        sub0 master0 monad0 [[Either UnFlaggedDay b0]]
         Actual type: GGHandler
                        sub0 master0 monad0 [Either UnFlaggedDay b0]
       In the expression: return $ P.concat processedDays
       In the expression:
         do { processedDays <- ([(Left $ MkUFD day)] :)
                             <$>
                               flagScheduled rest;
                return $ P.concat processedDays }

Вы видели, что там произошло? Вот изменения, которые я сделал. Я передал processedDays concat, прежде чем передать return.

flagScheduled ((Left (MkUFD day)):rest) = do
   match <- runDB $ selectList [TestStartDate ==. Just day,
                                TestStatus /<-. [Passed,Failed]] []
   case (L.null match) of
      True -> do
               processedDays <- ([(Left $ MkUFD day)] :) <$> flagScheduled rest
               return $ P.concat processedDays
      False -> do
                let flaggedRange = (calcFlagged match)
                    product      = (testFirmware . snd . P.head) match
                processedDays <- (flagScheduled'
                                  ([(Left $ MkUFD day)] ++
                                   (L.take flaggedRange) rest) (show product) :) <$>
                                   (flagScheduled . L.drop flaggedRange) rest
                return $ P.concat processedDays
flagScheduled ((Right day):rest) = do
     processedDays <- ((Right $ day):) <$> flagScheduled rest
     return $ P.concat processedDays
flagScheduled _ = return []

Поэтому тот факт, что то, что выглядит как прямолинейное изменение, на самом деле таковым не является, указывает мне на то, что я на самом деле не понимаю, в чем проблема. Любые идеи?

Обновление: я внес изменения, предложенные Даниэлем, но получил эту ошибку:

Utils/BuildDateList.hs:169:37:
    Couldn't match expected type `[Either UnFlaggedDay t0]'
                with actual type `Either UnFlaggedDay b0'
    In the first argument of `(++)', namely `(Left $ MkUFD day)'
    In the first argument of `flagScheduled'', namely
      `((Left $ MkUFD day) ++ (P.take flaggedRange) rest)'
    In the first argument of `(:)', namely
      `flagScheduled'
         ((Left $ MkUFD day) ++ (P.take flaggedRange) rest) (show product)'

Обновление: эта проблема была решена, только чтобы выявить другие (похожие) проблемы. Я собираюсь воспользоваться советом, данным здесь, чтобы двигаться вперед с этим.


person Michael Litchard    schedule 08.12.2011    source источник
comment
Я бы сильно разбил это на несколько более коротких функций — их будет легче тестировать, легче поддерживать и легче адаптировать при изменении обстоятельств.   -  person Matt Fenwick    schedule 09.12.2011
comment
Эй, это хороший ассонанс... легче тестировать/легче поддерживать/легче адаптировать при изменении обстоятельств! Это станет моей мантрой.   -  person Tom Crockett    schedule 09.12.2011


Ответы (1)


Первый подозреваемый:

   case (L.null match) of
      True -> do
           processedDays <- ([(Left $ MkUFD day)] :) <$> flagScheduled rest
           return processedDays

Может быть, это следует читать

   case (L.null match) of
      True -> do
           processedDays <- ((Left $ MkUFD day) :) <$> flagScheduled rest
           return processedDays

? О, и начните с написания подписей типа. Это часто создает лучшие сообщения об ошибках.

person Daniel Fischer    schedule 08.12.2011
comment
Я попытался написать подпись типа. Как это обычно бывает с типами Йесод, я ошибся. Моя привычка заключалась в том, чтобы позволить системе типов разобраться с этим, как это почти всегда и происходит. - person Michael Litchard; 09.12.2011
comment
@MichaelLitchard Так как насчет смешанного подхода? Вы начинаете с легкой части, здесь flagScheduled ((Right day):rest) = .... Вы спрашиваете ghci, что это за тип, и заполняете все переменные типа, которые вы знаете, как заполнять. Следующее уравнение повторяется. - person Daniel Fischer; 09.12.2011
comment
Вам также необходимо изменить `((Left $ MkUFD day) ++ (P.take flaggedRange) rest)' на ((Left $ MkUFD day) : P.take flaggedRange rest) - person Louis Wasserman; 09.12.2011