Pourquoi l'application partielle Haskell ne fonctionne-t-elle pas ?
-
20-12-2019 - |
Question
Mon travail chez Haskell se présente sous la forme de retravailler des projets .Net F# dans Haskell pour le plaisir.
J'analyse un fichier de configuration Windows standard : une paire clé/valeur par ligne, la clé étant séparée de la valeur par =
.Ce fichier est très simple et direct, ce qui permet à mon code d'analyse d'être simple et direct, ce que j'aime.
Ma question est de savoir pourquoi l'application partielle ne fonctionne pas sur la dernière ligne de code ci-dessous.Cela fonctionne évidemment dans les lignes précédentes et dans d'autres fonctions.
module Configuration (Config (..), load) where
import Data.Char (isSpace)
import Data.List (isPrefixOf)
data Config = Config { aliases :: [String]
, headers :: [String] }
-- This is a naive implementation. I can't decide if I like it better than
-- trim = unpack . strip . pack.
trim :: String -> String
trim = reverse . dropSpaces . reverse . dropSpaces
dropSpaces :: String -> String
dropSpaces = dropWhile isSpace
split _ [] = []
split c cs = [takeWhile (/= c) cs] ++ split c (tail' $ dropWhile (/= c) cs)
where tail' [] = []
tail' (x:xs) = xs
load :: String -> Config
load text =
let ss = lines text
hs = map getValue $ getLines "Header" ss
as = split ',' $ getValue $ getLine "AliasList" ss
in Config { aliases=as, headers=hs }
where getLines p = filter (p `isPrefixOf`)
getValue = trim . drop 1 . dropWhile (/= '=')
getLine = head . getLines -- Why isn't partial application working here?
L'erreur que j'obtiens est la suivante :
Configuration.hs:30:29:
Couldn't match expected type `[c0]'
with actual type `[[a0]] -> [[a0]]'
Expected type: [a0] -> [c0]
Actual type: [a0] -> [[a0]] -> [[a0]]
In the second argument of `(.)', namely `getLines'
In the expression: head . getLines
Merci!
La solution
Ce n’est pas une application partielle, mais une composition fonctionnelle qui ne fonctionne pas.Vous ne pouvez pas transmettre 2 arguments à une fonction qui fait partie d'une composition de fonction.
Autres conseils
La première chose à noter est la signature de getLines
.Depuis p `isPrefixOf`
a du type (Eq a) => [a] -> Bool
, getLines p
a du type (Eq a) => [[a]] -> [[a]]
(en fonction du type de filter
).Ici [a]
semble être String
, donc getLines p
a du type [String] -> [String]
et p
semble donc avoir le type String
.Donc en fait, getLines
a du type String -> [String] -> [String]
.
Dernièrement, head
a un type (spécialisé) [String] -> String
, et vous essayez de le postcomposer avec getLines
.Je suppose que vous essayez de créer une fonction avec le type String -> [String] -> String
, Défini par \p ss -> head (getLines p ss)
.C'est cependant pas quoi head . getLines
est!
Pour voir cela, considérez f :: a -> b -> c
et g :: c -> d
(où je veux dire que le c
s sont du même type dans les deux signatures, donc je n'écris pas vraiment de signatures Haskell appropriées ici).Puisqu'on aime souvent penser à f
comme une « fonction de deux variables », on pourrait se tromper et penser à g . f
comme étant la fonction \x y -> g (f x y)
(taper a -> b -> d
).Ce n'est pas le cas:Voir par exemple cette question et ses réponses ou l'exemple concret dans cette réponse. Ou encore mieux :Regarder le type de (.)
et déterminez quoi g . f
ça doit être toi-même !(Indice: Chaque fonction prend précisément un argument.Quel est le type de f
c'est un argument ?)