当其中一个从类型函数'deduced'时,如何使用多个'Data'实例?



我正在尝试使以下代码工作(好吧,首先编译!

module Orexio.Radix where
import Data.Data
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Typeable
import Text.JSON.Generic
class Resource a where
  type Representation a :: *
  identifier :: Resource a => Identifier a
class Endpoint a where
  call :: Resource a => a -> Representation a
data Identifier a = Identifier [String] deriving (Show)
data Binding a = Binding (JSValue -> Either String JSValue)
bind :: (Data a, Resource a, Endpoint a, Data (Representation a)) => Binding a
bind = Binding (x -> binding $ query x)
  where binding query = fmap (x -> toJSON $ call x) (resultToEither query)
        query jsvalue = fromJSON jsvalue
{-- DEMO --}
data HelloWorld = HelloWorld { 
  name :: String
} deriving (Show, Typeable, Data)
instance Resource HelloWorld where
  type Representation HelloWorld = String
  identifier = Identifier ["helloworld"]
instance Endpoint HelloWorld where
  call r = "Hello " ++ name r

所以我必须使FlexibleContexts能够做Data (Representation a),但它仍然不起作用......

我有这个错误:

src/Orexio/Radix.hs:21:33:
    Could not deduce (Data a0) arising from a use of `query'
    from the context (Data a,
                      Resource a,
                      Endpoint a,
                      Data (Representation a))
      bound by the type signature for
                 bind :: (Data a, Resource a, Endpoint a,
                          Data (Representation a)) =>
                         Binding a
      at src/Orexio/Radix.hs:20:9-78
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Data HelloWorld -- Defined at src/Orexio/Radix.hs:29:29
      instance Data () -- Defined in `Data.Data'
      instance (Data a, Data b) => Data (a, b) -- Defined in `Data.Data'
      ...plus 42 others
    In the second argument of `($)', namely `query x'
    In the expression: binding $ query x
    In the first argument of `Binding', namely
      `( x -> binding $ query x)'

老实说,我在这里有点迷茫,我一定错过了什么,但是什么?

以下是我激活的其他扩展:DeriveDataTypeableExistentialQuantificationNoMonomorphismRestrictionTypeFamilies

提前感谢!

看起来

Binding a应该是一个将类型a的值转换为类型为 Representation a 或错误消息的值的函数。 但是,由于输入和输出是 JSON 编码的,因此它们都具有类型 JSValue ;他们的类型根本没有提到a

data Binding a = Binding (JSValue -> Either String JSValue)

没有信息指示这些JSValue代表的类型。

bind的定义中,编译器知道返回类型是Binding a,但该类型和JSValue类型之间没有链接。 特别是,编译器无法推断fromJSON应该返回a,而toJSON应该Representation a。 要修复此问题,请将显式类型签名添加到 bindingquery

有时让人们感到困惑的一个细节是如何告诉GHC类型变量范围。 这需要ScopedTypeVariables扩展。 将forall a.添加到 bind 的类型签名中,并将forall.添加到bind正文中的其他类型签名中,以便变量a的作用域正确。

类型错误的本质是这一行:"类型变量'a0'不明确"。

(免责声明:我试图避免在这个答案中使用行话。

为了了解这里发生了什么,我建议将bindingquery绑定浮动到顶层。他们成功键入检查您是否随后注释掉bind绑定。GHC 推断出以下类型。

*Orexio.Radix> :i query
query :: Data a => JSValue -> Result a
*Orexio.Radix> :i binding
binding ::
  (Data (Representation a), Endpoint a, Resource a) =>
  Result a -> Either String JSValue

您的错误本质上是由bind定义中的表达式x -> binding (query x)引起的。请注意,类型变量 a 仅出现在 binding 的域和 query 的范围中。因此,当您撰写它们时,会发生两件事。

  1. 类型变量确定;其"值"仍然未知。

  2. 无法
  3. 从组成。出于我们的非正式目的,该类型是 JSValue -> Either String JSValue .

GHC 引发错误是因为类型变量在组合过程中未确定(即 1),并且将来永远无法确定(2 的结果)。

Haskell中这个问题的一般形状通常被称为"show - read问题";在现实世界Haskell的第6章中搜索"歧义"。(有些人可能也称之为"太多的多态性"。

正如您和 Sjoerd 所确定的(RWH 章节解释),您可以通过在应用 binding 之前将类型归因于 query 的结果来修复此类型错误。在不知道您的语义的情况下,我假设您打算将这个"隐藏"类型变量aBinding类型构造函数的参数相同。因此,以下内容将起作用。

bind :: forall b.
 (Data b, Resource b, Endpoint b, Data (Representation b)) => Binding b
bind = Binding (x -> binding $ (query x :: Result b))

此归因通过将类型变量完全替换为 Result b 来消除类型变量a。请注意,与a不同,b保持未确定是可以接受的,因为它可以在顶级类型中访问;bind的使用可能各自决定b

第一种解决方案需要给bind一个明确的签名——这有时可能相当繁重。在这种情况下,由于单态限制,您可能已经需要该类型签名。但是,如果bind接受了一个参数(但仍表现出这种不明确的类型变量错误),您仍然可以通过使用如下所示的解决方案来依赖类型推断。

dummy_ResultType :: Binding a -> Result a
dummy_ResultType = error "dummy_ResultType"
bind () = result where
  result = Binding (x -> binding $ (query x `asTypeOf` dummy))
  dummy = dummy_ResultType result

(如果使用error让您担心,请参阅代理类型。

或者用一些惯用语换取直接性:

withArgTypeOf :: f x -> g x -> f x
withArgTypeOf x _ = x
bind () = result where
  result = Binding (x -> binding (query x `withArgTypeOf` result))

现在推理有效。

*Orexio.Radix> :i bind
bind ::
  (Data (Representation a), Data a, Endpoint a, Resource a) =>
  () -> Binding a

请放心,GHC 在类型检查后会快速确定定义实际上不是递归的。

呵。

相关内容

最新更新