我正在通过fp-course学习haskell。当我为StateT实现Functor时,发生了一个错误。
-- | Implement the `Functor` instance for @StateT s k@ given a @Functor k@.
--
-- >>> runStateT ((+1) <$> (pure 2) :: StateT Int List Int) 0
-- [(3,0)]
instance Functor k => Functor (StateT s k) where
(<$>) ::
forall k a b s. (a -> b)
-> StateT s k a
-> StateT s k b
(<$>) f st1 = StateT $ \s -> mapFunc <$> (runStateT st1 s)
where
mapFunc :: (a, s) -> (b, s)
mapFunc = \(a, s) -> (f a, s)
误差
src/Course/StateT.hs:48:32: error:
• Could not deduce (Functor k1) arising from a use of ‘<$>’
from the context: Functor k
bound by the instance declaration at src/Course/StateT.hs:43:10-42
Possible fix:
add (Functor k1) to the context of
the type signature for:
(<$>) :: (a -> b) -> StateT s1 k1 a -> StateT s1 k1 b
• In the expression: mapFunc <$> (runStateT st1 s)
In the second argument of ‘($)’, namely
‘\ s -> mapFunc <$> (runStateT st1 s)’
In the expression: StateT $ \ s -> mapFunc <$> (runStateT st1 s)
Failed, modules loaded: Course.Applicative, Course.Core, Course.ExactlyOne, Course.Functor, Course.List, Course.Monad, Course.Optional, Course.State.
为什么会这样?如何解决?
2条答案
按热度按时间p3rjfoxz1#
当使用
ScopedTypeVariables
扩展时,forall
关键字声明了新的类型变量,这些变量隐藏了任何具有相同名称的现有类型变量的定义,因此您的示例等效于:换句话说,即使你应该为函子类型
StateT s k
定义运算符(<$>)
(因为这是你正在编写的示例),你也在为不同的类型StateT s1 k1
定义。不过,这没关系,因为GHC将允许您给予“错误”类型
StateT s1 k1
的示例定义,如果它可以与所需类型StateT s k
统一的话。通过将特定类型s
与任意类型s1
统一,并将特定类型k
与任意类型k1
统一,GHC可以做到这一点。但是,这是假设您的定义类型检查。您在定义右侧使用的
(<$>)
具有(a -> b) -> k1 a -> k1 b
类型,因此需要k1
的Functor
示例。但是,即使GHC从示例头知道Functor k
示例,它以前也从未听说过k1
类型,而且您从未在类型签名中说过它有一个Functor
示例。如果你写的是:一切都将类型检查正常。
为了清楚起见,在这个定义中发生的事情是,你已经告诉GHC你将为来自示例头的特定
s
和k
的类型StateT s k
定义(<$>)
,但是你却为通用s1
和k1
定义了一个定义,与示例头中的s
和k
无关,并表明只要这个新的任意类型k1
有一个Functor
示例,您的定义就可以工作。GHC接受此定义并验证具有任意s1
和k1
的类型StateT s1 k1
可以与来自示例头的具有特定s
和k
的StateT s k
统一。既然它可以,它就接受你的定义。对于
StateT s k
类型,这是一种非常迂回的(<$>)
定义方式。相反,您应该只使用示例头中的特定s
和k
类型变量,并且通过在forall
中NOT将它们声明为新变量来实现这一点:rbpvctlc2#
你的实现很好。但是
(<$>)
并不是Functor
合约的一部分。它是在类外部定义的别名。Functor
合约需要fmap
。如果我们将您的实现更改为使用fmap
并删除无关的类型注解(instance
声明不需要),则可以获得工作代码,而无需更改函数的实际主体。