我通过实现一个简单的例子开始研究Monads,但是我的Monad实例没有编译。
我想做类似的事情:
add5 7 >>= add7
此代码必须返回 19 [(5 + 7)>> =(12 + 7)]
我实施的代码是:
newtype MyType a = MyType ( a -> a)
instance Monad MyType where
MyType comm >>= comm2 = MyType (\inp -> let
value = comm inp
MyType comm2' = comm2
in comm2' value)
return x = MyType (\input -> input)
add5 :: MyType Integer
add5 = MyType (\inp -> inp + 5)
add7 :: MyType Integer
add7 = MyType (\inp -> inp + 7)
当我在不使用绑定运算符的情况下调用add5
和add7
时(通过注释Monad实例块),它可以工作:
main = do
let MyType x = add5
let MyType y = add7
putStrLn $ show $ x $ y 7
输出错误是:
new1.hs:5:94:
Couldn't match expected type `a' with actual type `b'
`a' is a rigid type variable bound by
the type signature for
>>= :: MyType a -> (a -> MyType b) -> MyType b
at new1.hs:4:9
`b' is a rigid type variable bound by
the type signature for
>>= :: MyType a -> (a -> MyType b) -> MyType b
at new1.hs:4:9
In the first argument of `comm', namely `inp'
In the expression: comm inp
In an equation for `value': value = comm inp
new1.hs:6:97:
Couldn't match expected type `MyType t0'
with actual type `a -> MyType b'
In the expression: comm2
In a pattern binding: MyType comm2' = comm2
In the expression:
let
value = comm inp
MyType comm2' = comm2
in comm2' value
答案 0 :(得分:4)
它不能是Monad
,因为它甚至不是Functor
,因为你在逆变位置有一个类型变量。
这意味着你无法实现:
fmap :: (a->b)->MyType a->MyType b
您可以使用f :: a->b
将a->a
中的结果类型更改为a->b
,但您无法将该参数的类型更改为b->b
1}},这是构建MyType b
所必需的。
答案 1 :(得分:4)
我不确定你真正想做什么。如果您只是想获取代码示例
add5 7 >>= add7
通过以“明显”方式添加数字来工作并生成19
的结果,然后它很简单,并且任何 monad都可以。因此,我们可以选择最简单的monad,即“身份”monad:
newtype Id a = Id { runId :: a }
instance Monad Id where
return x = Id x
Id x >>= f = f x
请注意,此代码将在GHC 7.8中生成警告,因为将来Applicative
将成为monad的超类,您将不得不定义其他实例。对于这个例子,它们是无关紧要的,所以我将省略它们。
现在您可以定义add5
和add7
:
add5 :: Id Int
add5 n = return (n + 5)
add7 :: Id Int
add7 n = return (n + 7)
如果省略类型签名并询问GHCi,您会发现两个定义实际上都具有更通用的类型(Num a, Monad m) => a -> m a
。这就是我的意思,说你的例子适用于任何 monad。
您可以尝试在GHCi中使用它:
GHCi> :t add5 7 >>= add7
add5 7 >>= add7 :: Id Int
GHCi> runId (add5 7 >>= add7)
19
答案 2 :(得分:3)
你走错了路。 MyType
不能成为monad。
MyType
唯一可能的monad实例实现将非常简单,并且无法使add5 7 >>= add7
等于19
。
>>=
必须有
MyType a -> (a -> MyType b) -> MyType b -- remove newType
(a -> a) -> (a -> (b -> b)) -> (b -> b)
typechecks的唯一功能是
(MyType _) >>= _ = MyType (\input -> input)
与您的return
实施非常相似。我们通常在haskell中写id
而不是(\input -> input)
。
为什么我声称这是唯一的功能?
再次检查简化类型签名(a -> a) -> (a -> (b -> b)) -> (b -> b)
:如果没有输入a
,则无法评估>>=
,a -> a
和a -> (b -> b)
的参数。
这不符合monad法律:x >>= return = x
MyType (\x -> x + 1) >>= return
=MyType id
/=MyType (\x -> x + 1)
答案 3 :(得分:1)
你不需要monad来做你想做的事情。相反,您可以使用Arrows:
Prelude> :m + Control.Arrow
Prelude Control.Arrow> let add5 = (+5)
Prelude Control.Arrow> let add7 = (+7)
Prelude Control.Arrow> let add12 = add5 >>> add7
Prelude Control.Arrow> add12 7
19
对于函数实例,(>>>)
函数只是组合运算符(即(.)
),其参数被翻转。因此你可以这样做:
Prelude> let add5 = (+5)
Prelude> let add7 = (+7)
Prelude> let add12 = add7 . add5
Prelude> add12 7
19
已经有一个函数的monad实例。它被称为读者monad:What is the purpose of the Reader Monad?。
instance Monad ((->) r) where
return x = \_ -> x
f >>= g = \x -> g (f x) x
它允许你这样的事情:
a2-minus-b2 = \a -> do
a-minus-b <- (\b -> a - b)
a-plus-b <- (\b -> a + b)
return (a-minus-b * a-plus-b)
当然,最好把它写成:
a2-minus-b2 = \a b -> (a - b) * (a + b)
但是,我只想告诉你读者monad可以用来做什么。