在研究了Haskell中的monads之后 - 这个主题非常引人注目 - 我想知道我是否可以在不使用已经定义的类型类的情况下自己定义monad。
我没有让Monad
成为Functor
的实例,而只是想用它自己的fmap
函数来定义monad本身(我还想更改一些函数名称,例如: return
并将其称为unit
)。
monad可以由绑定运算符(>>=)
和函数return
定义,但也可以用return
和join
来定义,因为最后一个函数它可以用绑定运算符表示:join m = m >>= id
。因此,monad可以(技术上)用return
和join
来定义,而不是别的。函数fmap
是必需的(并且是Haskell中Functor
存在的基础),但也可以用return
来定义,因为它也可以定义(我认为)如下所示:fmap f m = m >>= return . f
(在编辑之前,fmap f m = return . f
;这显然是一个错字)。
但我知道这不会像使用预定义的Monad
类型类一样高效,只是为了更好地理解Haskell语言。
我怎样才能做到这一点?现在,这是我头脑中对这个概念的描述,所以它不是有用的代码:
-- Just a sketch
infixr 9 ∘
(∘) :: (b -> c) -> (a -> b) -> a -> c
(∘) g f x = g (f x)
--(f ∘ g) x = f (g x)
-- My own 'fmap'
--mapper id = id
--mapper (f ∘ g) = mapper f ∘ mapper g
-- My monad
class MyMonadBase (m :: * -> *) where
unit :: a -> m a --return
join :: m (m a) -> m a
join = (>>= id)
mapper f m = m >>= unit ∘ f
--Testing:
data Tree a = Leaf a | Branch (Tree a) (Tree a)
instance MyMonadBase Tree where
unit = Leaf
join (Leaf x) = x
join (Branch l r) = Branch (join l) (join r)
我是否在正确的轨道上(概念上)?
答案 0 :(得分:3)
一些小的修正:
monad可以由绑定运算符
(>>=)
和函数return
定义,但也可以用return
和join
来定义,因为最后一个函数它可以用绑定运算符表示:join m = m >>= id
。
结论(“[a monad]也可以用return
和join
”来定义)是正确的,但前提(“自[join
]可以表达在绑定运算符方面“)并不意味着它。相反,您必须证明您省略的运算符 - 绑定运算符 - 可以根据您拥有的内容 - return
和join
进行定义。一旦你尝试这样做,你就会明白为什么monad也是一个仿函数是如此重要:
m >>= f = join (fmap f m)
函数
fmap
是必需的(并且是Haskell中Functor
存在的基础),但也可以用return
来定义,因为它也可以定义(我认为)如下:fmap f m = return . f
这不是fmap
的正确定义 - 你确实应该怀疑,因为右边没有提到m
!正确的版本是:
fmap f m = m >>= return . f
但现在这是一个循环定义,因为上面我们计划用(>>=)
来定义fmap
。因此,如果您想return
和join
Monad
的基本操作,您确实需要单独实施fmap
。
我知道这不如使用预定义的
那么有效Monad
类型类
我认为没有理由相信这是先验的。选择(>>=)
而不是join
的原因并不是因为它更有效,而是因为在编程中使用monad时它是如此自然的操作。
至于你的实际代码,我觉得它有两点需要注意:
我强烈怀疑你对mapper
的定义不符合你的想法。在第mapper id = id
行中,左侧的模式id
匹配所有传入的值,右侧的变量id
使它们保持不变。行mapper (f ∘ g) = mapper f ∘ mapper g
根本不是有效的Haskell。 (也许这两行旨在记录mapper
所需法律而非实际代码?)
以join
的形式提供mapper
和(>>=)
的默认实现很奇怪 - 两者都是由于上述原因(所有三个都是基本的,必须定义)并且因为(>>=)
不是类操作,因此用户无法以有意义的方式定义。
答案 1 :(得分:2)
好吧,这并不困难。我误以为实现一个自己的monad类型会非常复杂,但它只是应用了这个定义。
-- My monad
class MyMonad m where
unit :: a -> m a
join :: m (m a) -> m a
mapf :: (a -> b) -> m a -> m b
--Testing MyMonad
data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving (Show)
instance MyMonad Tree where
unit = Leaf
join (Leaf x) = x
join (Branch l r) = Branch (join l) (join r)
mapf f (Leaf x) = Leaf (f x)
mapf f (Branch l r) = Branch (mapf f l) (mapf f r)
t = Branch (Branch (Leaf 1) (Leaf 3)) (Branch (Leaf 2) (Leaf 4))
-- My bind (just for completeness, not that I need it for this example)
(>>>) :: MyMonad m => m a -> (a -> m b) -> m b
xs >>> f = join (mapf f xs)
-- Testing my bind
extr :: Integer -> Tree Integer
extr x = Branch (Leaf (x^2)) (Leaf (2^x))
t >>> extr
--Branch (Branch (Branch (Leaf 1) (Leaf 2)) (Branch (Leaf 9) (Leaf 8)))
-- (Branch (Branch (Leaf 4) (Leaf 4)) (Branch (Leaf 16) (Leaf 16)))