2017-10-05 16 views
12

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.

+6

Och, to jest świetne. Zauważ, że jeśli umieścisz niepoprawną definicję 'writeLine' _before_, inne definicje znika błąd. :) – Alec

+4

Mam otworzyć [GHC TraC# 14327] (https://ghc.haskell.org/trac/ghc/ticket/14327), aby śledzić ten problem. –

+0

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

Odpowiedz