GHC TypeLits没有值

Nic*_*gez 3 haskell ghc

试图设计一个类型驱动的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
Run Code Online (Sandbox Code Playgroud)

该程序将打印'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
Run Code Online (Sandbox Code Playgroud)

启用AllowAmbiguousTypes并没有多大帮助.无论如何都有办法让这个工作?

Tik*_*vis 9

在类型检查期间,类型同义词(定义为type)将替换为其定义.问题是在其定义中Printer没有引用s,这导致以下约束:

generic :: KnonwSymbol s => IO ()
Run Code Online (Sandbox Code Playgroud)

此类型签名没有s权限,=>因此无法进行歧义检查.它无法真正起作用,因为无法指定s使用它时应该是什么.

不幸的是,GHC在错误消息中表示类型同义词的方式不一致.有时它们会被扩展,有时它们会被保留.具有讽刺意味的是,我认为错误消息的改进使得这个特定错误更难以追踪:通常,根据您定义的类型表达错误更清楚,但这里隐藏了歧义的原因.

您需要的是提供不依赖于类型同义词的相关类型级符号的某种方式.但首先,您需要启用ScopedTypeVariables并添加一个forall签名,generic以确保s类型签名和sin Proxy :: Proxy s中的相同.

有两种可能性:

  • 更改Printer为a 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
    
    Run Code Online (Sandbox Code Playgroud)
  • 传递一个额外的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))
    
    Run Code Online (Sandbox Code Playgroud)

    有proxy一个类型的变量是一个整洁的成语,让你不依赖Data.Proxy,并让呼叫者通过在任何他们想要代替它.