GHC TypeLits没有值
试图设计一个类型驱动的API,我一直试图得到像下面这样的工作(使用更复杂的代码/尝试,这被剥离到最低限度要求澄清我在找什么):
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
module Main where
import Data.Proxy
import GHC.TypeLits
type Printer (s :: Symbol) = IO ()
concrete :: Printer "foo"
concrete = generic
generic :: KnownSymbol s => Printer s
generic = putStrLn (symbolVal (Proxy :: Proxy s))
main :: IO ()
main = concrete
这个程序会打印'foo',但不会:
Could not deduce (KnownSymbol s0)
arising from the ambiguity check for ‘generic’
from the context (KnownSymbol s)
bound by the type signature for
generic :: KnownSymbol s => Printer s
at test5.hs:14:12-37
The type variable ‘s0’ is ambiguous
In the ambiguity check for:
forall (s :: Symbol). KnownSymbol s => Printer s
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the type signature for ‘generic’:
generic :: KnownSymbol s => Printer s
启用AllowAmbiguousTypes
并没有真正的帮助。 任何方式来让这个工作无论如何?
在类型检查期间,类型同义词(用type
定义)被它们的定义替换。 问题在于Printer
在其定义中没有引用s
,这导致了以下约束:
generic :: KnonwSymbol s => IO ()
此类签名没有=>
s
权限,因此未能进行歧义检查。 它不能真正发挥作用,因为没有地方指定什么s
应该是,当你使用它。
不幸的是,GHC在错误消息中表示类型同义词的方式不一致。 有时它们会扩大,有时会被保留下来。 具有讽刺意味的是,我认为错误信息的改进使得这个特定的错误难以追踪:通常,根据您定义的类型表达错误更加清晰,但这里隐藏了歧义的原因。
您需要的是提供不依赖于类型同义词的相关类型级别符号的某种方式。 但首先,你需要启用ScopedTypeVariables
并添加forall
到的签名generic
以确保s
在类型签名和s
的Proxy :: Proxy s
是相同的。
有两种可能性:
更改Printer
的newtype
,当你使用它解开它:
newtype Printer (s :: Symbol) = Printer { runPrinter :: IO () }
generic :: forall s. KnownSymbol s => Printer s
generic = Printer $ putStrLn (symbolVal (Proxy :: Proxy s))
main = runPrinter generic
传递一个额外的Proxy
参数为generic
,就像symbolVal
一样:
concrete :: Printer "foo"
concrete = generic (Proxy :: Proxy "foo")
generic :: forall proxy s. KnownSymbol s => proxy s -> IO ()
generic _ = putStrLn (symbolVal (Proxy :: Proxy s))
把proxy
作为一个类型变量是一个整洁的习惯用法,它可以让你不依赖于Data.Proxy
并且让调用者代替他们想要的任何东西。
这是错误的:
generic :: KnownSymbol s => Printer s
generic = ...(Proxy :: Proxy s)
最后s
无关用s
上面。 与顶级类型注释一样,它在本地隐含地被普遍量化。 代码实际上意味着
generic :: KnownSymbol s => Printer s
generic = ...(Proxy :: forall z. Proxy z)
要解决上述问题,请启用ScopedTypeVariables
并使用
-- the explicit forall makes s available below
generic :: forall s. KnownSymbol s => Printer s
generic = ...(Proxy :: Proxy s)
然而,还有其他问题,正如吉洪维斯在答案中指出的那样。
链接地址: http://www.djcxy.com/p/90823.html上一篇: GHC TypeLits without values
下一篇: Android camera code