在自定义数据类型上定义和使用单子结构?
Defining and using monadic structures on custom datatypes?
我正在学习函子、应用程序和单子。
这些示例只是为了说明基础知识:
data User a = Age a deriving (Show, Eq, Ord)
Functor(将非上下文函数应用于单个上下文数据类型):
instance Functor User where
fmap f (Age a) = Age (f a)
functorFunction :: Int -> Int
functorFunction e = e + 1
main = do
print $ fmap functorFunction (Age 22)
Applicative(将简单函数应用于多个上下文数据类型):
instance Applicative User where
pure a = Age a
(<*>) (Age a) = fmap a
applicativeFunction :: Int -> Int -> Int
applicativeFunction e1 e2 = e1 + e2
main = do
print $ pure (applicativeFunction) <*> (Age 44) <*> (Age 65)
我浏览了 learnyouahaskell,但没能找到
的简单解释
1) 如何为我的 'User a' 类型定义 monad 结构,以及 2) monad 相对于应用仿函数提供什么功能?
1) how to define a monad structure for my 'User a' type
(初步说明:Learn You a Haskell 早于 Applicative
成为 Monad
的超类。因此,与书中建议的不同,最新版本的 GHC 有不需要实现return
-- 默认情况下,它与pure
相同。既然如此,我将直接跳转到(>>=)
。)
关于具有非常通用类型的方法的一个好处,例如 (>>=)
...
(>>=) :: Monad m => m a -> (a -> m b) -> m b
... 是因为它们太笼统以至于几乎没有实现它们的明智方法——通常只有一种。如果我们专门针对您的类型 (>>=)
,我们得到:
(>>=) @User :: User a -> (a -> User b) -> User b
所以 (>>=)
应该采用 User a
和 a -> User b
并使用它们生成 User b
。鉴于该函数采用 a
值,首先要尝试寻找 a
值传递给它。我们确实有这样一个值:被 Age
构造函数包装在 User a
值中的值:
instance Monad User where
(Age a) >>= f = f a
为了良心编写一个 Monad
实例,我们应该检查它是否遵循 monad 法则:
return a >>= f = f a
m >>= return = m
(m >>= f) >>= g = m >>= (\x -> f x >>= g)
这可以用(你最喜欢的等价物)笔和纸来完成:
return a >>= f = f a
Age a >>= f = f a
f a = f a
m >>= return = m
Age a >>= return = Age a -- [*]
Age a >>= Age = Age a
Age a = Age a
-- [*]: Both here and in the next law, we can replace 'm' by 'Age a'
-- because there is just one constructor. If there were more
-- (as in e.g. data Maybe a = Nothing | Just a), we would have
-- to check the law holds for all constructors.
(m >>= f) >>= g = m >>= (\x -> f x >>= g)
(Age a >>= f) >>= g = Age a >>= (\x -> f x >>= g)
f a >>= g = (\x -> f x >>= g) a
f a >>= g = f a >>= g
2) what functionality a monad provides versus, say, an applicative functor?
这里留给the question arrowd suggested。我只会注意到您的示例不足以让您真正体会到差异,因为 User
上下文太简单了,无法让您使用 (>>=)
做任何有趣的事情(正如本杰明霍奇森指出的那样,它同构于 Identity
,虚拟函子)。 Maybe
被链接问题的公认答案所使用,更具说明性。
我正在学习函子、应用程序和单子。
这些示例只是为了说明基础知识:
data User a = Age a deriving (Show, Eq, Ord)
Functor(将非上下文函数应用于单个上下文数据类型):
instance Functor User where
fmap f (Age a) = Age (f a)
functorFunction :: Int -> Int
functorFunction e = e + 1
main = do
print $ fmap functorFunction (Age 22)
Applicative(将简单函数应用于多个上下文数据类型):
instance Applicative User where
pure a = Age a
(<*>) (Age a) = fmap a
applicativeFunction :: Int -> Int -> Int
applicativeFunction e1 e2 = e1 + e2
main = do
print $ pure (applicativeFunction) <*> (Age 44) <*> (Age 65)
我浏览了 learnyouahaskell,但没能找到
的简单解释1) 如何为我的 'User a' 类型定义 monad 结构,以及 2) monad 相对于应用仿函数提供什么功能?
1) how to define a monad structure for my 'User a' type
(初步说明:Learn You a Haskell 早于 Applicative
成为 Monad
的超类。因此,与书中建议的不同,最新版本的 GHC 有不需要实现return
-- 默认情况下,它与pure
相同。既然如此,我将直接跳转到(>>=)
。)
关于具有非常通用类型的方法的一个好处,例如 (>>=)
...
(>>=) :: Monad m => m a -> (a -> m b) -> m b
... 是因为它们太笼统以至于几乎没有实现它们的明智方法——通常只有一种。如果我们专门针对您的类型 (>>=)
,我们得到:
(>>=) @User :: User a -> (a -> User b) -> User b
所以 (>>=)
应该采用 User a
和 a -> User b
并使用它们生成 User b
。鉴于该函数采用 a
值,首先要尝试寻找 a
值传递给它。我们确实有这样一个值:被 Age
构造函数包装在 User a
值中的值:
instance Monad User where
(Age a) >>= f = f a
为了良心编写一个 Monad
实例,我们应该检查它是否遵循 monad 法则:
return a >>= f = f a
m >>= return = m
(m >>= f) >>= g = m >>= (\x -> f x >>= g)
这可以用(你最喜欢的等价物)笔和纸来完成:
return a >>= f = f a
Age a >>= f = f a
f a = f a
m >>= return = m
Age a >>= return = Age a -- [*]
Age a >>= Age = Age a
Age a = Age a
-- [*]: Both here and in the next law, we can replace 'm' by 'Age a'
-- because there is just one constructor. If there were more
-- (as in e.g. data Maybe a = Nothing | Just a), we would have
-- to check the law holds for all constructors.
(m >>= f) >>= g = m >>= (\x -> f x >>= g)
(Age a >>= f) >>= g = Age a >>= (\x -> f x >>= g)
f a >>= g = (\x -> f x >>= g) a
f a >>= g = f a >>= g
2) what functionality a monad provides versus, say, an applicative functor?
这里留给the question arrowd suggested。我只会注意到您的示例不足以让您真正体会到差异,因为 User
上下文太简单了,无法让您使用 (>>=)
做任何有趣的事情(正如本杰明霍奇森指出的那样,它同构于 Identity
,虚拟函子)。 Maybe
被链接问题的公认答案所使用,更具说明性。