4

我想为列表单子提供我自己的实例。不幸的是,以下内容在编译时会导致重复实例声明错误。

myReturn :: a -> [a]
myBind :: [a] -> (a -> [b]) -> [b]
instance Monad [] where
    return = myReturn
    (>>=) = myBind

从文档来看,导入时似乎无法隐藏实例声明,并且由于列表 monad 实例已经在前奏中声明,我想我也无法摆脱导入本身。

我想也许我至少可以重新绑定(>>=)return这样我就可以使用我自己的实现来使用 do 块,因为 do 块据说只是(>>=)and应用程序的语法糖(>>)

let
    return = myReturn
    (>>=) = myBind
in
    do
        item1 <- list1
        item2 <- list2
        return (item1, item2)

不幸的是,块似乎是(>>=)从其他地方获取它们的,因为它仍在使用(>>=)默认列表 monad 实例。

有什么方法可以让我的实现(>>=)return一个 list monad 的实例,或者至少有一种方法可以将它们与 do 块一起使用?

4

2 回答 2

6

您不能Monad为列表定义另一个实例,在某些情况下您可以定义一个新类型来解决这个问题,但是您必须手动将所有列表函数提升到新类型才能使用它们。

要仅使用您自己的(>>=)return在 do-blocks 中,您可以使用带有 GHC 的语言扩展:

{-# LANGUAGE NoImplicitPrelude #-}
module ListMon where

import Prelude hiding ((>>=), return)

(>>=) :: [a] -> (a -> [b]) -> [b]
xs >>= foo = case xs of
               [] -> [undefined]
               [x] -> foo x ++ foo x
               ys -> take 10 $ concatMap foo $ take 5 ys

return :: a -> [a]
return x = [x,x,x]

someList :: [Int]
someList = do
    k <- [1 .. 4]
    h <- [2 .. 3]
    return (k + 12*h)

导致

$ ghci ListMon
{- snip loading messages -}
[1 of 1] Compiling ListMon          ( ListMon.hs, interpreted )
Ok, modules loaded:
*ListMon> someList 
[25,25,25,37,37,37,26,26,26,38,38,38,27,27,27,39,39,39,28,28,28,40,40,40]

随着NoImplicitPrelude, do-notation 的脱糖使用任何东西(>>=)并且return在范围内。

于 2012-07-23T00:15:10.593 回答
4

您可以将列表包装在新类型中

newtype MyList a = MyList { unMyList :: [a] }

并为此包装类型声明您的实例

instance Monad MyList where
    return = MyList . myReturn
    (MyList m) >>= f = MyList . myBind m f

现在只需将列表包装在 newtype 包装器中MyList(newtypes 只是语法糖,因此在编译时被删除)。

于 2012-07-23T00:11:57.587 回答