Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Haskell中定义一个新单子?_Haskell_Operator Overloading_Monads - Fatal编程技术网

在Haskell中定义一个新单子?

在Haskell中定义一个新单子?,haskell,operator-overloading,monads,Haskell,Operator Overloading,Monads,我想在Haskell中创建我自己的monad,并让Haskell像对待其他内置monad一样对待它。例如,下面是创建单子的代码,该单子每次调用时都会更新一些全局状态变量,还有一个计算器,使用它计算调用quot函数的次数: -- define the monad type type M a = State -> (a, State) type State = Int -- define the return and bind operators for this monad return

我想在Haskell中创建我自己的monad,并让Haskell像对待其他内置monad一样对待它。例如,下面是创建单子的代码,该单子每次调用时都会更新一些全局状态变量,还有一个计算器,使用它计算调用
quot
函数的次数:

-- define the monad type
type M a = State -> (a, State)
type State = Int

-- define the return and bind operators for this monad
return a x = (a, x)
(>>=) :: M a -> (a -> M b) -> M b
m >>= k = \x -> let (a,y) = m x in
                let (b,z) = k a y in
                (b,z)

-- define the tick monad, which increments the state by one
tick :: M ()
tick x = ((), x+1)

data Term = Con Int | Div Term Term
-- define the evaluator that computes the number of times 'quot' is called as a side effect
eval :: Term -> M Int
eval (Con a)   = Main.return a
eval (Div t u) = eval t Main.>>= \a -> eval u Main.>>= \b -> (tick Main.>>= \()->Main.return(quot a b))

answer :: Term
answer = (Div (Div (Con 1972)(Con 2))(Con 23))

(result, state) = eval answer 0

main = putStrLn ((show result) ++ ", " ++ (show state))

现在执行时,
return
>=
属于命名空间
Main
,我必须将它们与
Prelude.return
Prelude.>=
区分开来。如果我想让Haskell像对待任何其他类型的单子一样对待
M
,并在
Prelude
中适当地重载单子操作符,我该如何做呢?

让你的新单子与所有现有的Haskell机器一起工作--
符号,例如——您所需要做的就是将您的类型声明为
Monad
typeclass的实例。然后
Prelude
函数
>=
return
等将与所有其他
Monad
类型一样适用于您的新类型

不过,有一个限制,需要对示例进行一些更改。类型同义词(用
Type
声明)不能成为类实例。(您的
ma
Int->(a,Int)
完全相同)您需要使用
data
newtype
。(这两者之间的区别与此无关。)

这两个关键词创造了一个真正的新类型;特别是,它们创建了一个新的数据构造函数。你应该在哈斯凯尔的任何一篇基础文章中读到这一点。简而言之,
newtypexa=Y(…)
创建一个新类型
xa
;您可以使用构造函数
Y
(它可以而且通常与类型构造函数
X
具有相同的名称)创建该类型的值;您可以通过
Y
上的模式匹配来使用值。如果选择不导出数据构造函数
Y
,则只有模块中的函数才能直接操作值

(有一个GHC扩展
TypeSynonymInstances
,但它在这里对您没有帮助,因为有一个单独的问题:不能部分应用类型同义词;对于任何
Typexa={-…-}
你只能写
xa
xint
或诸如此类的东西,而不能只写
X
。你不能写
实例Monad M
,因为
M
是部分应用的。)

之后,您只需将
return
>=
的定义移动到
实例Monad
声明中:

newtype M a = M (State -> (a, State))

instance Monad M where
  return a = M $ \x -> (a, x)
  m >>= k = {- ... -}

请注意,
(>>=)
的实现有点冗长,因为您需要使用其数据构造函数
M
打开并重新包装
newtype
。请看,它使用了一个记录访问器来简化。(您可以手动编写一个函数
runM::M->State->(a,State)
相当于
transformers
和许多其他软件包使用的记录语法。)

下面是一个实现:

    -- Otherwise you can't do the Applicative instance.
    import Control.Applicative


    -- Simple function
    foo :: String -> String
    foo x = do
        x ++ "!!!"

    -- Helper for printing Monads
    print2 :: (Show a) => MyBox a -> IO()
    print2 (MyBox x) = print x


    -- Custom type declaration
    data MyBox a = MyBox a


    -- MyBox functor
    instance Functor MyBox where  
        fmap f (MyBox x) = MyBox (f x)


    -- MyBox Applicative 
    instance Applicative MyBox where  
        pure = MyBox 
        (MyBox f) <*> x = f <$> x


    -- MyBox Monad
    instance Monad MyBox where  
        return x = MyBox x
        MyBox x >>= f  = f x 

    -- (MyBox as a functor) Use a function with a wrapped value
    result1 = foo <$> (MyBox "Brian")

    -- (MyBox as an Applicative) Use a wrapped function with a wrapped value
    result2 = (MyBox foo) <*> (MyBox "Erich")

    -- (MyBox as a Monad)  Use a wrapped value with a lambda (it can be chainable)
    myLambda1 = (\ x -> MyBox (x ++  " aaa"))
    myLambda2 = (\ x -> MyBox (x ++  " bbb"))
    myLambda3 = (\ x -> MyBox (x ++  " ccc"))
    result3 = (MyBox "Rick") 
        >>= myLambda1
        >>= myLambda2
        >>= myLambda3


    -- Another Monad syntax
    result4 = do
        x <- MyBox "A" 
        y <- MyBox "B"  
        z <- MyBox "C"  
        MyBox (x ++ y ++ z)


    main = do
        print2(result1) -- "Brian!!!"
        print2(result2) -- "Erich!!!"
        print2(result3) -- "Rick aaa bbb ccc"
        print2(result4) -- "ABC"
——否则您无法执行应用程序实例。
导入控制
--简单函数
foo::String->String
foox=do
x++“!!!”
--打印单子的助手
打印2::(显示a)=>MyBox a->IO()
打印2(MyBox x)=打印x
--自定义类型声明
数据MyBox a=MyBox a
--MyBox函子
实例函子MyBox,其中
fmap f(MyBox x)=MyBox(fx)
--MyBox应用程序
实例应用程序MyBox,其中
纯=我的盒子
(MyBox f)x=f x
--MyBox单子
实例Monad MyBox,其中
返回x=MyBox x
MyBox x>>=f=f x
--(MyBox作为函子)使用具有包装值的函数
结果1=foo(MyBox“Brian”)
--(MyBox作为应用程序)使用带包装值的包装函数
结果2=(MyBox foo)(MyBox“Erich”)
--(MyBox作为Monad)使用带lambda的包装值(可以链接)
myLambda1=(\x->MyBox(x++“aaa”))
myLambda2=(\x->MyBox(x++“bbb”))
myLambda3=(\x->MyBox(x++“ccc”))
结果3=(MyBox“Rick”)
>>=myLambda1
>>=myLambda2
>>=myLambda3
--另一种单子语法
结果4=do

x是否同样适用于
newtype M a=(State->(a,State))
,或者
newtype
在某种程度上与
type
不同,因此有必要在右侧添加
M
?@DavidPfau
newtype
不同。您必须使用
M
构造函数显式地包装和展开内容,因为您正在创建一个新类型,该类型不会作为包装类型进行类型检查。链接部分涉及制作单子。