允许只有相同的Monad类型与`>>`运算符连接的逻辑是什么?

时间:2017-11-28 13:31:47

标签: haskell monads

虽然可以绑定IO [[Char]]IO (),但不允许将Maybe绑定到IO。有人可以举例说明这种放松会导致糟糕的设计吗?为什么允许Monad的多态类型中的自由而不是Monad本身呢?

2 个答案:

答案 0 :(得分:7)

有许多好的理论原因,包括“那不是Monad。”但是,让我们暂时离开它,看看实现细节。

首先关闭 - Monad不是魔法。它只是一个标准的类型。 Monad的实例只有在有人写一个实例时才会创建。

编写该实例是定义(>>)如何工作的原因。通常它是通过(>>=)方面的默认定义隐式完成的,但这只是(>>=)是更一般的运算符的证据,编写它需要做出写(>>)的所有相同决策需要。

如果你有一个不同的运算符可以处理更常见的类型,你必须回答两个问题。首先,这些类型是什么?其次,您将如何提供实施?从你的问题来看,真正不清楚所需的类型是什么。我猜是以下之一:

class Poly1 m n where
    (>>) :: m a -> n b -> m b

class Poly2 m n where
    (>>) :: m a -> n b -> n b

class Poly3 m n o | m n -> o where
    (>>) :: m a -> n b -> o b

所有这些都可以实施。但是你实际上失去了两个非常重要的因素。

  1. 您需要为计划一起使用的每对类型编写一个实例。这是一项比每种类型的实例更复杂的事业。关于nn^2的对比。
  2. 你失去了可预测性。操作甚至做什么?这是理论和实践相交的地方。 Monad背后的理论对操作施加了很多限制。这些限制被称为“单一法律”。它们超出了在Haskell中验证的能力,但任何不遵守它们的Monad实例都被认为是错误的。最终结果是,您可以快速建立对Monad操作执行和不执行操作的直觉。您可以在不查看所涉及的每种类型的详细信息的情况下使用它们,因为您知道它们遵守的属性。我建议的那些可能的课程都没有给你任何类似的保证。你根本不知道他们做了什么。

答案 1 :(得分:0)

我不确定我是否正确理解了您的问题,但绝对有可能将MaybeIO[]组合起来,就像您可以撰写{{1}一样与IO

例如,如果使用[]检查GHCI中的类型,

:t

为您提供getContents >>= return . lines 。如果你添加

IO [String]

您获得的 >>= return . map Text.Read.readMaybe 类型由IO [Maybe a]IO[]组成。然后你可以把它传递给

Maybe

将其展平为 >>= return . Data.Maybe.catMaybes 。然后,您可以将已解析的有效输入行列表传递给再次展平它的函数,并计算输出。

把这个放在一起,程序

IO [a]

输入:

import Text.Read (readMaybe)
import Data.Maybe (catMaybes)

main :: IO ()
main = getContents >>=                    -- IO String
       return . lines >>=                 -- IO [String]
       return . map readMaybe >>=         -- IO [Maybe Int]
       return . catMaybes >>=             -- IO [Int]
       return . (sum :: [Int] -> Int) >>= -- IO Int
       print                              -- IO ()

打印1 2 Ignore this! 3

也可以使用6IO (Maybe [String])等。

您也可以使用Maybe [IO String]执行此操作。受控示例:>>读取输入,忽略它,并返回getContents >> (return . Just) False