【问题标题】:Getting confused with complex Haskell typeclass declarations对复杂的 Haskell 类型类声明感到困惑
【发布时间】:2016-06-18 07:07:48
【问题描述】:

我有一个 monad 转换器堆栈的类型别名:

type KStat s a = ReaderT (KStatRoot s) (ExceptT KindError (ST s)) a

我需要将用户从这种类型中抽象出来,主要是因为KStatRoot 结构导致了循环依赖。因此,我创建了一个单独的模块并为它定义了一个类型类:

class (Monad (m s), MonadError KindError (m s)) =>
      MStat m s where
    liftToST :: ST s a -> m s a
    kstatNewRef :: a -> m s (STRef s a)
    kstatReadRef :: STRef s a -> m s a
    kstatWriteRef :: STRef s a -> a -> m s ()

这个定义可以编译(虽然需要{-# LANGUAGE MultiParamTypeClasses,FlexibleContexts #-} 才能工作,但我明白为什么这两个都是必需的),我已经能够将一些使用站点转换为类型类并让它们进行类型检查,所以一切那里似乎还可以。但是我正在努力弄清楚如何为该类定义我的实例:

instance MStat (KStat s a) s where
    liftToST = lift . lift
    kstatNewRef = liftToST . newSTRef
    kstatReadRef = liftToST . readSTRef
    kstatWriteRef r v = liftToST $ writeSTRef r v

给我错误:

src/KindLang/Data/KStat.hs:27:17:
    The first argument of ‘MStat’ should have kind ‘* -> * -> *’,
      but ‘KStat s a’ has kind ‘*’
    In the instance declaration for ‘MStat (KStat s a) s’

哪种有意义,但是如果我在实例标头中将KStat s a 更改为KStat,我会收到此错误:

src/KindLang/Data/KStat.hs:27:10:
    Type synonym ‘KStat’ should have 2 arguments, but has been given none
    In the instance declaration for ‘MStat KStat s’

这似乎基本上是在说完全相反的。

我在声明实例的模块中使用这些语言扩展:

{-# LANGUAGE RankNTypes, TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses  #-}

如何解决这些错误?

显示错误的完整文件如下:

{-# LANGUAGE RankNTypes, TypeSynonymInstances, FlexibleContexts,
  FlexibleInstances, MultiParamTypeClasses  #-}

import Control.Monad.Except
import Control.Monad.ST
import Control.Monad.Reader
import Data.STRef

data KStatRoot s = KStatRoot
data KindError

class (Monad (m s), MonadError KindError (m s)) =>
      MStat m s where
    liftToST :: ST s a -> m s a
    kstatNewRef :: a -> m s (STRef s a)
    kstatReadRef :: STRef s a -> m s a
    kstatWriteRef :: STRef s a -> a -> m s ()

type KStat s a = ReaderT (KStatRoot s) (ExceptT KindError (ST s)) a

instance MStat (KStat s m) s where
    liftToST = lift . lift
    kstatNewRef = liftToST . newSTRef
    kstatReadRef = liftToST . readSTRef
    kstatWriteRef r v = liftToST $ writeSTRef r v

【问题讨论】:

  • 你能提供一个完整的导入示例吗?
  • @ErikR - 所涉及的完整文件相当大,但如果你能给我一些线索你想要达到的目标,也许我可以解决一些问题?
  • 只举一个简单的例子来说明问题。
  • @ErikR - 好的,添加了一个模块,所有内容都在同一个地方,给出了相同的错误。

标签: haskell typeclass


【解决方案1】:

第一个错误是“正确的”(您需要在实例声明中使用两个参数的类型),您尝试的修复是有意义的。

但是,type 同义词如果没有它的参数就不会真正存在。也就是之后

type Foo a = ...

您不能单独使用FooFoo 必须应用于参数才能被类型检查器处理。这是您的第二个错误的原因。

我看到的唯一解决方法是将KStat 更改为newtype

newtype KStat s a = KStat{ runKStat :: ReaderT (KStatRoot s) (ExceptT KindError (ST s)) a }

这将让您使用KStat 不带参数。您只需在各处添加明确的runKStat/KStat 转换。

【讨论】:

  • 我开始认为这是解决方案。寻找其他人做同样事情的代码告诉我,几乎每个人都使用newtype 而不是type 用于任何非平凡的单子类型......幸运的是,我已经在使用函数来抽象运行单子,所以这应该是一个很好的简单改变。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-25
  • 2019-02-13
  • 2012-12-23
  • 1970-01-01
  • 1970-01-01
  • 2017-03-22
相关资源
最近更新 更多