适用性

时间:2019-07-13 21:03:20

标签: haskell applicative

我是Haskell的初学者,我必须解决工作表问题,这次我真的很困。帮助!:))

我必须计算某个monad的实例,从functor到monad,而我无法在Applicative实例中弄清楚其纯粹性。我不想发布程序的所有代码,但我认为以下应该可以说明问题:

这是给定的代码,无法更改:

class (Semigroup a, Monoid a) => Log a where
    logMsg :: String -> a

newtype Logger l a = Logger (a,l)
    deriving Show

logMsgM :: Log l => String -> Logger l ()
logMsgM s = Logger ((), logMsg s)

这些是我正在处理的实例:

instance (Log l) => Functor (Logger l) where 
    fmap g (Logger (a, l)) = Logger ((g a), l)


instance (Log l) => Applicative (Logger l) where 
    pure ???????????????????
    (<*>) (Logger (g,l)) (Logger (a,_)) = Logger ((g a), l)

instance (Log l) => Monad (Logger l) where 
    return                                              = pure
    (>>=) (Logger (a,l)) g                              = (g a)
    (>>)  x y                                           = x >>= \_ -> y

我被卡住了。 pure = Logger会产生无限类型错误a〜(a,l),所有使用输入参数进行调整的尝试都将以未知变量错误或带有尾随零等类型变量的奇怪类型错误而告终。我做了很多尝试,很难以更明智的方式发布错误消息。 我不了解发生了什么。这是基于入门讲座的,所以我了解基本概念。我只是无法使用函数声明(我认为...)正确处理此新类型定义,尤其是将两个参数归为一个(对)的情况。我确实理解(或更恰当的措辞:“我可以遵循”)带有类型定义或新类型的案例的典型示例,例如may,any或其他在右侧具有相同数量或更多参数的案例。但是,也许我对此假设有误,并在一个根本看不到的地方犯了谬论。

顺便说一句,编写pure = return时,整个代码都会编译,但是程序会产生堆栈溢出(微笑)。我认为这也就不足为奇了,因为return = puer和pure = return可以很好地编译,但是没有任何意义,对!!

感谢帮助:)

1 个答案:

答案 0 :(得分:6)

Logger是一种更常见的类型,称为Writer monad。这个想法是,如果您有一个Logger l a,其中l是一个Semigroup,那么如果您先运行Logger l a然后再运行另一个Logger l b,结果将是使两个l彼此相邻(即使用Monoid操作串联)。那就是:

Logger (1,"test1") >> Logger (2,"test2")   ==   Logger (2,"test1" <> "test2")

鉴于此,pure a将只是Logger (a, something),其中something是一个在连接时不起作用的值。但是现在来看一下typeclass声明:

class (Semigroup a, Monoid a) => Log a where ...
instance (Log l) => Applicative (Logger l) where ...

因此,在Applicative (Logger l)中,l必须是Monoid,因此它必须具有标识值mempty!这样就得到了pure a = Logger (a, mempty),它只返回了a而又不影响日志。


但是,仍然存在问题。如果我们有Logger (a1,l1) >>= \x -> Logger (a2,l2)Logger (a1,l1) <*> Logger (a2,l2),我们想在结果中串联l1 <> l2。但是您当前的实现无法做到这一点!因此,您需要对其进行更改才能满足此属性。由于我不想为您解决整个工作表,因此我将其保留为练习;但是,如果您陷入困境,则可能需要查阅此答案顶部的链接。