当我遇到一个我并不理解的好奇的GHC行为时,我正在向同事展示Free
。这是一个简单的,人为的程序,可以说明:
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 ()))
我尝试添加另一个定义,但它不太正确:
writeLine :: String -> App ()
writeLine line = liftF (InR (WriteLine line ()))
这里的问题是我错过了另一个InL
。正确的定义应该是:
writeLine :: String -> App ()
writeLine line = liftF (InR (InL (WriteLine line ())))
然而,这不是重点。对我来说奇怪的是GHC在添加writeLine
的第一个错误定义时产生的类型错误。具体来说,它产生了两个类型的错误:
/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 ()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
两个错误中的第二个(第29行的错误)是有道理的。这是我期望的错误。但第26行的错误完全让我感到困惑。 writeFile
的定义是正确的!添加writeLine
的错误定义不应对writeFile
产生任何影响,对吧?发生了什么事?
我能够在GHC 8.0.2和GHC 8.2.1上重现这一点。我想知道这是否是GHC中的一个错误(所以我可以报告)或者我的代码是否存在我不明白的问题。
答案 0 :(得分:8)
这是一个GHC错误。 Ryan Scott表示,它已在提交48daaaf0bba279b6e362ee5c632de69ed31ab65d (Don't report fundep wanted/wanted errors)中修复。