Haskell 如果我们用它来写mfix,liftM对函子实例是否太严格了?

Haskell 如果我们用它来写mfix,liftM对函子实例是否太严格了?,haskell,monads,functor,strictness,Haskell,Monads,Functor,Strictness,fmap对于monad通常默认为: 我的直觉是是的,它可以被使用。我估计我们只在两种情况下遇到问题: k我们将mfix/myMfix应用于一个严格的系统 我们将mfix/myMfix应用于return 这两种情况都相当简单,我们一开始并不期望有任何收敛的结果。我相信其他案例可以在不强制反馈的情况下被强制WHNF 我的直觉正确吗?如果没有,你能举一个失败的例子吗?你对Id'的定义不是有效的单子,因为它违反了左单位定律: 返回未定义>=const(返回3) =Id'未定义>>=const(返回3)

fmap
对于
monad
通常默认为:

我的直觉是是的,它可以被使用。我估计我们只在两种情况下遇到问题:

  • k
    我们将
    mfix
    /
    myMfix
    应用于一个严格的系统
  • 我们将
    mfix
    /
    myMfix
    应用于
    return
  • 这两种情况都相当简单,我们一开始并不期望有任何收敛的结果。我相信其他案例可以在不强制反馈的情况下被强制WHNF


    我的直觉正确吗?如果没有,你能举一个失败的例子吗?

    你对
    Id'
    的定义不是有效的单子,因为它违反了左单位定律:

    返回未定义>=const(返回3)
    =Id'未定义>>=const(返回3)
    =未定义的`seq`const(返回3)未定义
    =未定义
    
    这不等于

    const(返回3)未定义
    =返回3
    =Id'3
    
    因此,关于
    Id'
    MonadFix
    定义是否有效的讨论相当没有意义,更不用说通过Kleisli构造的定义是否可信了

    话虽如此,如果你想知道一个定义是否是mfix的忠实实现,你需要写下并证明严格、纯洁和收缩三条定律。如果你从一个实际的单子开始(而不是你的
    Id'
    ,它没有通过测试),那么你需要建立这三个定律,将其视为一个有效的单子反馈算子。如果不做这项工作,就不可能说这个定义是如何概括的

    我的直觉是,通过Kleisli构造的定义直觉可能非常适用于行为良好的箭头,即那些
    循环
    操作符确实满足正确的拧紧法则的箭头。然而,众所周知,具有左严格绑定运算符的monad的Kleisli类上的箭头没有满足右紧的
    循环
    运算符。(见第6.4.1节中的说明和讨论。)


    因此,任何具有左严格绑定运算符的monad(例如
    []
    可能
    IO
    ,严格状态等)都将无法通过测试。但是由monad(如环境、惰性状态)和由monoid(也称为writer monad)生成的箭头可能会起作用。

    我猜你的意思是
    Id'
    ,而不是代码中的
    Maybe'
    。@Krantz是的。谢谢-我编辑了这篇文章。
    mfix
    总是需要特别小心才能实现。不知道你能说多少,这将普遍适用。谢谢你的回答。然而,你在最后一篇摘录中提到的“克莱斯利·蒙纳德”是什么?此外,当你说我的直觉不适用于某些单子时,这是否意味着我们可能无法从
    mfix
    循环中为一个符合
    Kleisli m
    的单子提取信息?我不明白你在第一条评论中询问的是哪一个单子。关于你的第二个评论:我的意思是考虑任何具有左严格绑定操作符的Haskell monad。(可能是IO、List、Strict state…)为其构造Kleisli箭头。该箭头将不具有合适的循环运算符。也就是说,您不能为满足箭头循环法则的对象定义循环,因为正确的拧紧将失败。因此,实际上没有相应的
    mfix
    可提取。关于第一个:您正在写:“但是,众所周知,从具有左严格绑定运算符的Kleisli单子产生的箭头没有”。什么是克莱斯里单子?关于第二个:即使某些实例不满足定律,但这并不意味着我们不想对这些单子使用定点(-like)组合符。问题是,如果我们尝试将我们的组合符应用于
    (1:)
    ,我们就可以马上得到WHNF。问题是当我们没有这样的案例时,这可能会如何影响反馈。我看到了混乱。我应该说“左严格绑定操作符的单子的Kleisli类上的箭头…”希望现在已经清楚了。相应地编辑了文本。关于
    mfix
    /
    loop
    等的非守法定义:您肯定可以定义任何您认为有用的函数。我可以看到一系列的研究削弱了
    mfix
    /
    loop
    法则,以允许更多的定义。如果你能想出一个更为宽容的抽象,它仍然是有用的,那将是值得探索的。但就我所知,这方面的研究基本上还没有涉足。(老实说,我不确定它是否非常有趣。)
    liftM   :: (Monad m) => (a1 -> r) -> m a1 -> m r
    liftM f m1 = do { x1 <- m1; return (f x1) }
    
    newtype Id' a = Id' { runId' :: a } deriving ...
    
    instance Functor Id' where
        fmap = liftM -- instead of `f <$> (Id' x) = Id' (f x)`. 
    
    instance Applicative Id' where
        pure  = return
        (<*>) = ap
    
    instance Monad Id' where
        return         = Id'
        (Id' x)  >>= k = x `seq` k x
    
    instance MonadFix Id' where
        mfix f = let a = f (runId' a) in a
    
    myMfix :: MonadFix m => (a -> m a) -> m a
    myMfix k = let f ~(_, d) = ((,) d) <$> k d
               in (flip runKleisli () . loop . Kleisli) f