Redefine list monad instance

一曲冷凌霜 提交于 2019-12-10 17:52:35

问题


I'd like to supply my own instance for the list monad. Unfortunately, the following causes a duplicate instance declaration error when compiling.

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

From the documentation, it seems like it's not possible to hide instance declarations when importing, and since the list monad instance is already declared in the prelude, I guess I can't get rid of the import itself either.

I figured that maybe I could at least rebind (>>=) and return so that I would be able to use do blocks using my own implementation since do blocks are supposedly just syntactic sugar for applications of (>>=) and (>>).

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

Unfortunately, it seems like do blocks get their (>>=) from somewhere else, because it's still using the (>>=) of the default list monad instance.

Is there any way to make my implementations of (>>=) and return an instance of list monad, or at least a way to use them with do blocks?


回答1:


You cannot define another Monad instance for lists, in some circumstances you can define a newtype to work around that, but you'd have to manually lift all list functions to the newtype to use them.

To just use your own (>>=) and return in do-blocks, you can use a language extension with 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)

resulting in

$ 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]

With NoImplicitPrelude, desugaring of do-notation uses whatever (>>=) and return are in scope.




回答2:


You can wrap the list in a newtype

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

and declare your instance for this wrapped type

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

Now simply wrap your lists in the newtype wrapper MyList (newtypes are just syntactic sugar and are thus removed at compilation).



来源:https://stackoverflow.com/questions/11605027/redefine-list-monad-instance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!