Pokazałem współpracownika Free
, gdy wpadłem na dziwne zachowanie GHC, którego tak naprawdę nie rozumiem. Oto prosty, wymyślony program typechecks:Dlaczego pojawia się błąd typu w tym programie, gdy dodaję niepowiązaną definicję?
import Prelude hiding (readFile, writeFile)
import Control.Monad.Free
import Data.Functor.Sum
data FileSystemF a
= ReadFile FilePath (String -> a)
| WriteFile FilePath String a
deriving (Functor)
data ConsoleF a
= WriteLine String a
deriving (Functor)
data CloudF a
= GetStackInfo String (String -> a)
deriving (Functor)
type App = Free (Sum FileSystemF (Sum ConsoleF CloudF))
readFile :: FilePath -> App String
readFile path = liftF (InL (ReadFile path id))
writeFile :: FilePath -> String -> App()
writeFile path contents = liftF (InL (WriteFile path contents()))
Próbowałem dodając inną definicję, ale nie miał rację:
writeLine :: String -> App()
writeLine line = liftF (InR (WriteLine line()))
Problem polega na tym, że brakowało mi inny InL
. Prawidłowa definicja powinna być następująca:
writeLine :: String -> App()
writeLine line = liftF (InR (InL (WriteLine line())))
Jednak nie o to chodzi. Co jest dla mnie dziwne, to błąd typu, który wytworzył GHC, kiedy dodałem swoją pierwszą, niepoprawną definicję writeLine
. Konkretnie, to produkowane dwa błędy Typ:
/private/tmp/free-sandbox/src/FreeSandbox.hs:26:27: error:
• Couldn't match type ‘ConsoleF’ with ‘Sum ConsoleF CloudF’
arising from a functional dependency between constraints:
‘MonadFree
(Sum FileSystemF (Sum ConsoleF CloudF))
(Free (Sum FileSystemF (Sum ConsoleF CloudF)))’
arising from a use of ‘liftF’ at src/FreeSandbox.hs:26:27-66
‘MonadFree
(Sum FileSystemF ConsoleF)
(Free (Sum FileSystemF (Sum ConsoleF CloudF)))’
arising from a use of ‘liftF’ at src/FreeSandbox.hs:29:18-48
• In the expression: liftF (InL (WriteFile path contents()))
In an equation for ‘writeFile’:
writeFile path contents = liftF (InL (WriteFile path contents()))
|
26 | writeFile path contents = liftF (InL (WriteFile path contents()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/private/tmp/free-sandbox/src/FreeSandbox.hs:29:18: error:
• Couldn't match type ‘Sum ConsoleF CloudF’ with ‘ConsoleF’
arising from a functional dependency between:
constraint ‘MonadFree
(Sum FileSystemF ConsoleF)
(Free (Sum FileSystemF (Sum ConsoleF CloudF)))’
arising from a use of ‘liftF’
instance ‘MonadFree f (Free f)’ at <no location info>
• In the expression: liftF (InR (WriteLine line()))
In an equation for ‘writeLine’:
writeLine line = liftF (InR (WriteLine line()))
|
29 | writeLine line = liftF (InR (WriteLine line()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Drugi z dwóch błędów (jeden na linię 29) ma sens. To błąd, którego mógłbym się spodziewać. Ale błąd w linii 26 całkowicie mnie zaskakuje. Definicja writeFile
jest poprawna! Dodanie mojej niepoprawnej definicji writeLine
nie powinno mieć żadnego wpływu na writeFile
, prawda? Co się dzieje?
Udało mi się odtworzyć to na GHC 8.0.2 i GHC 8.2.1. Chciałbym wiedzieć, czy jest to błąd w GHC (mogę go zgłosić), czy też jest to problem z moim kodem, którego nie rozumiem.
Och, to jest świetne. Zauważ, że jeśli umieścisz niepoprawną definicję 'writeLine' _before_, inne definicje znika błąd. :) – Alec
Mam otworzyć [GHC TraC# 14327] (https://ghc.haskell.org/trac/ghc/ticket/14327), aby śledzić ten problem. –
Problem wydaje się być związany z FD, ponieważ specjalizacja 'liftF ':: Functor f => f a -> Free f a' pozbywa się dodatkowego błędu. Może coś mi brakuje. – Alec