首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在TypeClass中使用代理

在TypeClass中使用代理
EN

Stack Overflow用户
提问于 2018-06-29 13:01:58
回答 1查看 169关注 0票数 3

我正在尝试做一些类似于this question的事情。

我想定义一个典型的

代码语言:javascript
复制
class Wrapper f where
    wrap :: a -> f a
    unwrap :: f a -> a
    name :: Proxy (f a) -> String

然后定义

代码语言:javascript
复制
instance (IsString a, FromJSON a, Wrapper f) => FromJSON (f a) where
    parseJSON (String s) = wrap <$> pure (fromString $ unpack s)
    parseJSON invalid    = typeMismatch (name (Proxy :: Proxy (f a))) invalid

但我说错了

代码语言:javascript
复制
Could not deduce (Wrapper f0) arising from a use of ‘name’                                               
      from the context: (IsString a, FromJSON a, Wrapper f)                                                    
        bound by the instance declaration at src/Model/Wrapper.hs:29:10-62                                     
      The type variable ‘f0’ is ambiguous

我不太清楚为什么这不起作用,如果可能的话,可以用某种方式修复它。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-06-29 13:23:00

首先,我要说几句:

  1. 不要定义这样的实例。这个实例将匹配任何形式的f a,而不管f实际上是否在Wrapper类中。特别是,它还将与标准实例(如FromJSON (Vector a) )发生冲突,尽管Vector不能是Wrapper的(行为良好)实例。之所以如此,是因为Haskell的类型系统是基于一个开放世界的假设:编译器永远不能假设某个类型不在某个类中,因为至少从技术上讲,任何人都可以在以后添加该实例。
  2. 我建议不要在新代码中使用Proxy。我一直认为Proxy是一种丑陋的黑客,几乎不像undefined :: T参数那样丑陋,这些参数在过去的Haskell代码中经常被使用。在新的GHC中,-XAllowAmbiguousTypes-XTypeApplications正确地解决了这个问题;这些允许您只进行签名。 {-#语言AllowAmbiguousTypes #-}类包装器f.名称: String 然后,代替name (Proxy :: Proxy (f a)),只编写name @f

现在来看一下实际的问题:您的代码不能工作,因为标准Haskell中的类型变量总是只属于单个类型签名/类上下文,但是在定义它的代码中是不可用的。IOW,类型变量不使用与值变量相同的名称范围,这就是为什么当您提到Proxy (f a)时,编译器会将类型变量“消歧”到f0a0。这是Haskell98的一个愚蠢的缺点,并且是由-XScopedTypeVariables扩展(连同 aka forall关键字)所加进去的。以下内容将自行汇编:

代码语言:javascript
复制
{-# LANGUAGE ScopedTypeVariables, UnicodeSyntax #-}
instance ∀ f a .  (IsString a, FromJSON a, Wrapper f) => FromJSON (f a) where
    parseJSON (String s) = wrap <$> pure (fromString $ unpack s)
    parseJSON invalid    = typeMismatch (name (Proxy :: Proxy (f a))) invalid

就像我说过的那样,不应该定义这样一个实例。我觉得你真正想要的是

代码语言:javascript
复制
{-# LANGUAGE DataKinds, KindSignatures, TypeApplications #-}

import GHC.TypeLits (Symbol, KnownSymbol, symbolVal)

data Wrapper (n :: String) (a :: *)
   = Wrapper a
   | TypeMismatch String

instance ∀ a s . (IsString a, FromJSON a, KnownSymbol s)
                     => FromJSON (Wrapper s a) where
  parseJSON (String s) = Wrapper <$> pure (fromString $ unpack s)
  parseJSON invalid    = TypeMismatch $ symbolVal @s Proxy

不需要上课。

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51102411

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档