在 Haskell 中定义 monad

Defining a monad in Haskell

提问人:Julia 提问时间:8/30/2023 更新时间:8/30/2023 访问量:104

问:

我目前正在阅读 Kan Extensions for Program Optimisation,在论文的第一页,作者定义了给定 M 一个单子,即以下单子

type C a = ∀z . (a → M z ) → M z
instance Monad C where
  return a = λc → c a
  m >>= k = λc → m (λa → k a c)

所以我尝试使用 M=Maybe 来玩一下它,因此编写了以下代码

type C a = forall z . (a -> Maybe z) -> Maybe z

instance Monad C where
  return a = \c -> c a
  m >>= k = \c -> m $ \a -> k a c

但它不编译和输出

Main.hs:22:10:错误: • 类型同义词“C”应该有 1 个参数,但没有得到任何参数

我无法解释。这里有什么问题?(我做过一些范畴理论,但我才刚刚开始学习Haskell)。

哈斯克尔 单子

评论

3赞 Fyodor Soikin 8/30/2023
在 Haskell 类型中,由于复杂的技术原因,同义词不能部分应用。让它成为 INSTEAD。newtype
0赞 Julia 8/30/2023
@FyodorSoikin 我之前已经(天真地)尝试过这样做,但我遇到了另一个错误。“无法分析数据/newtype 声明中的数据构造函数”
1赞 Fyodor Soikin 8/30/2023
好吧,我猜你在语法上打错了字
1赞 Ben 8/30/2023
@Julia 您不能只将关键字替换为 . 更相似 ;您需要在右侧定义其构造函数,而不仅仅是编写类型表达式。typenewtypenewtypedatatype
1赞 Ben 8/30/2023
@Julia 研究论文通常用伪代码而不是实际代码给出他们的例子,这样他们就不必担心 和 之间的区别,而可以写下想法。这个例子是伪 Haskell(lambda 是一个死的赠品; 可以给你花哨的箭头,但不是)。如果你想编译类似的东西,你需要先把它翻译成实际的Haskell。我可以谦虚地建议,“我刚刚开始学习 Haskell”不是将研究论文中的想法转化为工作代码的正确经验水平吗?typenewtypeUnicodeSyntaxλ

答:

7赞 Silvio Mayolo 8/30/2023 #1

如评论中所述,您需要一个 . 别名只是程序员的便利,不是不同的实体,你想要实现一个类型类,所以你需要一个不同的实体。你可以这样写。newtypetypenewtype

newtype C a = C (forall z . (a -> Maybe z) -> Maybe z)

虽然我建议包含一个访问器,因为我们稍后会需要它进行封送。

newtype C a = C { unC :: forall z . (a -> Maybe z) -> Maybe z }

(如果你在家里跟着做,你需要能够执行这个语法。它是一个编译器扩展,支持该特定的更高级类型){-# LANGUAGE RankNTypes #-}forall

需要明确的是,我们不会为 编写实例。这种类型不属于我们。我们将为 ,为我们刚刚编造的一个新类型编写一个实例,它恰好以一种很好的方式同构。Monadforall z. (a -> Maybe z) -> Maybe zMonadC aforall z. (a -> Maybe z) -> Maybe z

现在 Monad 依赖于 FunctorApplicative,所以我们将从那里开始。

Functor可派生的,所以我们可以直接写,但知道如何写出这些也是很好的做法。deriving (Functor)

instance Functor C where
    fmap f (C a) = C $ \k -> a (k . f)

现在。根据相应的实例实现是很常见的,因此我们将在这里执行此操作。ApplicativeApplicativeMonad

instance Applicative C where
    pure = return
    (<*>) = ap

(注意:来自模块,所以你也要导入它)apControl.Monad

这始终有效,前提是您编写实例时不调用 或 .Monadpure<*>

现在 .它几乎与您编写的内容完全相同,除了一些和编组以使类型检查器满意。MonadCunC

instance Monad C where
    return a = C (\c -> c a)
    m >>= k = C (\c -> unC m $ \a -> unC (k a) c)

最后,请注意,我们从未在这里实际使用过它的任何属性。我们不仅不在乎那是什么,我们甚至不需要它成为.因此,我们也可以通过它进行参数化。MaybeMFunctor

{-# LANGUAGE RankNTypes #-}

import Control.Monad

newtype C m a = C { unC :: forall z . (a -> m z) -> m z }

instance Functor (C m) where
    fmap f (C a) = C $ \k -> a (k . f)

instance Applicative (C m) where
    pure = return
    (<*>) = ap

instance Monad (C m) where
    return a = C (\c -> c a)
    m >>= k = C (\c -> unC m $ \a -> unC (k a) c)

Now 与您刚才使用的类型相同,但我们可以用任何种类来代替 .任何事情,而不仅仅是 s. 狂野。C MaybeC* -> *mFunctor

1赞 willeM_ Van Onsem 8/30/2023 #2

您不能使用部分应用于实例的类型别名。在这种情况下,通常要做的是使用 a 创建一个新类型,但本质上只是它包装的类型:newtype

{-# LANGUAGE RankNTypes #-}
{-# Language UnicodeSyntax #-}

newtype C m a = C { getC :: ∀z . (a → m z ) → m z}

然后将实例定义为:

instance Monad m => Monad (C m) where
  return a = C (\c -> c a)
  C m >>= k = C (\c -> m (\a -> getC (k a) c))

因此,它在构造函数中进行了一些包装和解包,但这些本质上是无操作的。C