与复杂的Haskell类型类声明混淆

时间:2016-06-18 07:07:48

标签: haskell typeclass

我有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

1 个答案:

答案 0 :(得分:4)

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

但是,如果没有参数,type同义词就不存在。也就是说,

之后
type Foo a = ...

您不能单独使用Foo。必须将Foo应用于参数才能由类型检查器处理。这是导致第二次错误的原因。

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

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

这将允许您在没有参数的情况下使用KStat。您只需在任何地方添加明确的runKStat / KStat转化。