如何使newtype成为monad的一个实例

Kha*_*aab -1 haskell

我被困在这里,我可以制作data类型实例,Monad但我无法弄清楚我是如何做到的newtype.

newtype Val a = Val {getVal :: [a]} deriving (Show)

instance Monad Val where
    return = Val
    (>>=) (Val {getVal = l}) f  = map f l
Run Code Online (Sandbox Code Playgroud)

如果我这样写:

instance Monad Val where
return = Val
(>>=) (Val {getVal = l}) f  =  {getVal = map f l}
Run Code Online (Sandbox Code Playgroud)

然后我得到错误,它说parse error on input { .

我如何制作newtypeMonad的实例?

Sil*_*olo 5

这里有几个问题.首先,您必须在大括号之前指定记录名称.所以你要

(>>=) (Val {getVal = l}) f = Val {getVal = map f l}
Run Code Online (Sandbox Code Playgroud)

现在,你的任何一个函数都不会立即进行类型检查.但修复并不太难,因为您只是委托已经存在的列表monad实例.你return很接近,但你需要将结果包装在一个列表中.

return x = Val [x]
Run Code Online (Sandbox Code Playgroud)

同样,你不想map在记录里面.你想要列表monad版本(>>=).

(>>=) (Val {getVal = l}) f = Val {getVal = l >>= f}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这仍然不会严重,因为f它旨在返回Val而不是[].我们需要在monad操作中进行修正.

(>>=) (Val {getVal = l}) f = Val {getVal = l >>= getVal . f}
Run Code Online (Sandbox Code Playgroud)

在那之后,你可能会Applicative因为成为超类而感到错误Monad.这可以通过一些辅助函数的简单应用来解决.

import Control.Monad

-- ...

instance Functor Val where
    fmap = liftM

instance Applicative Val where
    pure = return
    (<*>) = ap
Run Code Online (Sandbox Code Playgroud)

现在一切都应该编译.


作为旁注,如果你正在使用GHC,那么有一个方便的功能GeneralizedNewtypeDeriving可以自动为你提供newtype数据类型的实例.你可以像这样使用它

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype Val a = Val {getVal :: [a]}
    deriving (Show, Functor, Applicative, Monad)
Run Code Online (Sandbox Code Playgroud)

这只适用于newtype,而不是data,它只适用于GHC,因此如果您希望您的代码可以跨编译器移植,请不要使用它.