Während ich mit Haskell und Conduit spielte, stieß ich auf ein Verhalten, das ich schwer erklären kann. Lassen Sie mich zunächst eine Liste aller Module und Spracherweiterungen, die mein Problem zu ladende müssen zu reproduzieren:Typeclass Constraints, Polymorphismus und Maniok-Conduit
{-# LANGUAGE FlexibleContexts #-}
import Conduit -- conduit-combinators
import Data.Csv -- cassava
import Data.Csv.Conduit -- cassava-conduit
import qualified Data.ByteString as BS -- bytestring
import Data.Text (Text) -- text
import Control.Monad.Except -- mtl
import Data.Foldable
Zuerst habe ich die allgemeinste CSV-Parsing Leitung:
pipeline :: (MonadError CsvParseError m, FromRecord a)
=> ConduitM BS.ByteString a m()
pipeline = fromCsv defaultDecodeOptions NoHeader
Dann wollte ich Ausgang die Anzahl der Elemente in jeder Reihe meiner CSV-Datei - ich weiß, dass das ziemlich albern und nutzlos ist und dass es eine Milliarde anderer Möglichkeiten gibt, diese Art von Dingen zu tun, aber das war nur ein Spielzeugtest. So
Ich öffnete GHCi und versuchte dies:
ghci> :t pipeline .| mapC length
Wie erwartet, dies nicht funktioniert, weil die Einschränkung FromRecord a
garantiert nicht, dass a
Foldable
ist. So definierte ich die folgende Leitung:
pipeline2 :: (MonadError CsvParseError m, FromField a)
=> ConduitM BS.ByteString [a] m()
pipeline2 = fromCsv defaultDecodeOptions NoHeader
Dies ist eine rechtliche Definition, weil FromField a => FromField [a]
ist eine Instanz FromRecord
nach der Maniok Dokumentation.
An dieser Stelle bin ich glücklich und hoffnungsvoll, weil []
eine Instanz von Foldable
ist. Also, noch einmal, ich öffne GHCi, und ich versuche:
ghci> :t pipeline2 .| mapC length
Aber ich bekomme:
<interactive>:1:1: error:
• Could not deduce (FromField a0) arising from a use of ‘pipeline2’
from the context: MonadError CsvParseError m
bound by the inferred type of
it :: MonadError CsvParseError m => ConduitM BS.ByteString Int m()
at <interactive>:1:1
The type variable ‘a0’ is ambiguous
These potential instances exist:
instance FromField a => FromField (Either Field a)
-- Defined in ‘cassava-0.4.5.0:Data.Csv.Conversion’
instance FromField BS.ByteString
-- Defined in ‘cassava-0.4.5.0:Data.Csv.Conversion’
instance FromField Integer
-- Defined in ‘cassava-0.4.5.0:Data.Csv.Conversion’
...plus 9 others
...plus 11 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the first argument of ‘(.|)’, namely ‘pipeline2’
In the expression: pipeline2 .| mapC length
Also mein Verständnis ist, dass meine pipeline2
angegeben ist nicht genug.
Aber jetzt, wenn ich versuche, eine triviale Leitung mit einem (fast) gleichen Typs zu schmieden:
pipeline3 :: (MonadError CsvParseError m, FromField a)
=> ConduitM a [a] m()
pipeline3 = awaitForever $ \x -> yield [x]
Wieder ich GHCi öffnen und versuchen:
ghci> :t pipeline3 .| mapC length
Diesmal bekomme ich:
Diesmal versteht GHCi, dass ich die Definition vonnicht noch weiter spezifizieren muss.
Also meine Frage: Warum gibt es ein Problem mit pipeline2
? Gibt es eine Möglichkeit, die allgemeinste "Pipeline" zu definieren, ohne die Art der Ausgabe des Conduits näher zu spezifizieren? Ich dachte, dass eine Liste von FromField
Objekte würde ausreichen.
Es fühlt sich an, als ob ich einen wichtigen Punkt über Typklassen und wie man Funktionen komponiert, oder hier Conduit Objekte, in einer polymorphen Weise fehlt.
Vielen Dank für Ihre Antworten!
Danke, dass ich von 'TypeApplications' erinnert. Ich hob das für meine Antwort auf. – duplode