2016-12-28 12 views
0

Я использую GHCJSi, версию 0.2.0-7.10.3: http://www.github.com/ghcjs/ghcjs/ и версию библиотеки reflex-dom версии 0-4 от https://github.com/reflex-frp/reflex-dom. Я не использую reflex-dom-0.3 от Hackage.Haskell: Как исправить ошибку компилятора типа переменной?

Следующая программа Haskell не компилировать с рефлекторным-домом-0,4:

{-# LANGUAGE RecursiveDo, ScopedTypeVariables, DeriveGeneric, OverloadedStrings #-} 
import Reflex 
import Reflex.Dom 
import Data.Aeson 
import GHC.Generics 
import qualified Data.Text as T 
data Apod = Apod { copyright :: T.Text 
       , date :: T.Text 
       , explanation :: T.Text 
       , hdurl :: T.Text 
       , media_type :: T.Text 
       , service_version :: T.Text 
       , title :: T.Text 
       , url :: T.Text 
       } deriving (Generic, Show) 
instance FromJSON Apod 
main :: IO() 
main = do 
    mainWidget $ el "div" $ do 
    buttonEvent <- button "GET" 
    let url = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY" 
    let defaultReq = xhrRequest "GET" url def 
    asyncEvent <- performRequestAsync (tag (constant defaultReq) buttonEvent) 
    let rspApod = fmapMaybe (\r -> decodeXhrResponse r) asyncEvent 
    return() 

Я получаю ошибку

Xhr00.hs:24:36: 
    No instance for (aeson-0.9.0.1:Data.Aeson.Types.Class.FromJSON b0) 
     arising from a use of ‘decodeXhrResponse’ 
    The type variable ‘b0’ is ambiguous 
    Relevant bindings include 
     rspApod :: Event Spider b0 (bound at Xhr00.hs:24:9) 
    Note: there is a potential instance available: 
     instance (aeson-0.9.0.1:Data.Aeson.Types.Class.FromJSON a, 
       aeson-0.9.0.1:Data.Aeson.Types.Class.FromJSON b) => 
       aeson-0.9.0.1:Data.Aeson.Types.Class.FromJSON 
       (Data.These.These a b) 
     -- Defined in ‘Data.These’ 
     In the expression: decodeXhrResponse r 
    In the first argument of ‘fmapMaybe’, namely 
     ‘(\ r -> decodeXhrResponse r)’ 
    In the expression: 
     fmapMaybe (\ r -> decodeXhrResponse r) asyncEvent 
Failed, modules loaded: none. 

Я INLINE библиотечной функции рефлекторно-Dóm decodeXhrResponse (а также decodeText) , Я меняю подпись типа FromJSON a => XhrResponse -> Maybe a на подпись без переменной типа XhrResponse -> Maybe Apod. Затем программа успешно компилируется.

{-# LANGUAGE RecursiveDo, ScopedTypeVariables, DeriveGeneric, OverloadedStrings #-} 
import Reflex 
import Reflex.Dom hiding (decodeXhrResponse, decodeText) 
-- import Reflex.Dom 
import Data.Aeson 
import GHC.Generics 
import qualified Data.Text as T 
import Control.Monad 
import qualified Data.ByteString.Lazy as BL 
import Data.Text.Encoding 
data Apod = Apod { copyright :: T.Text 
       , date :: T.Text 
       , explanation :: T.Text 
       , hdurl :: T.Text 
       , media_type :: T.Text 
       , service_version :: T.Text 
       , title :: T.Text 
       , url :: T.Text 
       } deriving (Generic, Show) 
instance FromJSON Apod 
main :: IO() 
main = do 
    mainWidget $ el "div" $ do 
    buttonEvent <- button "GET" 
    let nasa = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY" 
    let defaultReq = xhrRequest "GET" nasa def 
    asyncEvent <- performRequestAsync (tag (constant defaultReq) buttonEvent) 
    let rspApod :: Event Spider Apod = fmapMaybe (\r -> decodeXhrResponse r) asyncEvent 
    return() 
-- Inlined and changed library function: 
-- decodeXhrResponse :: FromJSON a => XhrResponse -> Maybe a 
decodeXhrResponse :: XhrResponse -> Maybe Apod 
decodeXhrResponse = join . fmap decodeText . _xhrResponse_responseText 
-- Inlined and changed library function: 
-- decodeText :: FromJSON a => T.Text -> Maybe a 
decodeText :: T.Text -> Maybe Apod 
decodeText = decode . BL.fromStrict . encodeUtf8 

Я пытался добавить переменный контекстный тип для rspApod как rspApod :: Event t Apod или rspApod :: Event Spider Apod, но это не помогло.

Вопросы:

Как я должен изменить свою первую программу для успешной компиляции? (встраивание и изменение библиотечной функции - очень плохой взлом!)

Почему компилятор не находит и использует экземпляр FromJSON для типа данных Apod?

+0

'(\ r -> decodeXhrResponse r)' ~> 'decodeXhrResponse' –

+0

Ваша оригинальная программа неоднозначна. Как насчет просто добавления подписи типа 'decodeXhrResponse r :: Maybe Apod'. –

+0

@Reid: Спасибо, но изменив на 'let rspApod = fmapMaybe (\ r -> decodeXhrResponse r :: Возможно, Apod). AsyncEvent' не решает проблему: Дает такую ​​же ошибку« Нет экземпляра для ». К сожалению ... – Jogger

ответ

3

Так оригинальная подпись функции является

decodeXhrResponse :: FromJSON a => XhrResponse -> Maybe a

Таким образом, когда используется компилятор должен найти экземпляр FromJSON для данного a. В вашем случае a - Apod, поэтому компилятор должен штрафовать экземпляр FromJSON за Apod. В вашем коде компилятор не знает, что это ваше намерение. Это обычная проблема при разборе, когда компилятору нужно сообщить, какой тип цели должен быть.

Теперь вы можете утверждать, что он должен иметь возможность определять тип цели окружающим кодом, например asyncEvent, но это не по какой-то причине. Может быть, окружающий код такой же общий. Рассмотрим следующий сценарий:

main = print $ read x

Как компилятор знать тип цели для чтения x?

read :: Read a => String -> a Очевидно, это не информирует его о цели.

print :: Show a => a -> IO() и это просто утверждает, что a должен иметь экземпляр Show.

a является слишком общим для анализа, нам нужен конкретный тип.

Итак, когда вы вставляете функции и меняли сигнатуры типов, включив в них Apod, вы предоставили компилятору информацию, необходимую ему, чтобы узнать, что такое FromJSON экземпляр для поиска.

Вот как я бы решил эту проблему:

main :: IO() 
main = do 
    mainWidget $ el "div" $ do 
    buttonEvent <- button "GET" 
    let url = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY" 
    let defaultReq = xhrRequest "GET" url def 
    asyncEvent <- performRequestAsync (tag (constant defaultReq) buttonEvent) 
    let rspApod = fmapMaybe (\r -> decodeXhrResponse r :: Maybe Apod) asyncEvent 
    return() 

Добавление аннотацию типа :: Maybe Apod инлайн должен дать компилятору информацию, которую он должен знать, от предполагаемого разбора цели. Разумно использовать сигнатуры типов таким образом, так как это действительно эффективно.

Надеюсь, что это поможет!

+0

Большое спасибо за ваше любезное объяснение и ваше решение. Ваше решение такое же, как у Рида. К сожалению, это не помогает – Jogger

+0

Даже если я комментирую с обеих сторон 'let rspApod :: Event Spider (Maybe Apod) = fmap (\ r -> decodeXhrResponse r :: Maybe Apod) asyncEvent', я все равно получаю ошибку' No экземпляр для (aeson-0.9.0.1: Data.Aeson.Types.Class.FromJSON Apod), возникающий из-за использования 'decodeXhrResponse' – Jogger

+0

Я добавил дополнительный экземпляр FromJSON (возможно, Apod), и я получил 2 ошибки: «Перекрытие экземпляры для FromJSON (возможно, Apod) , возникающие из-за использования 'aeson-1.0.2.1: Data.Aeson.Types.FromJSON. $ gdmparseJSON'' и 'Нет экземпляра для (aeson-0.9.0.1: Data.Aeson.Types. Class.FromJSON Apod) '. Кажется, что есть 2 разных версии Data.Aeson 1.0 и 0.9 !! Как это возможно ?? – Jogger

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