Monads,"容器"来自sigfpe博客的练习

时间:2014-10-13 01:45:51

标签: haskell monads

我正在努力学习#34;了解你的Haskell"我现在正在关于monad的章节。在其他与Haskell相关的帖子中,我看到很多人推荐sigfpe在monad上的博客文章;强烈建议Haskell学生通过他的各种练习,让读者发明"发明" /"发现" monads的概念为他/她自己。

我在容器练习的最后一步遇到了麻烦。我认为链接到他的网站会违反论坛规则,所以我会尽力在这里描述它。 (如果我在这方面做错了,请注意。)作为一个抬头,我对练习的描述可能不完全一致,所以一个简单的谷歌搜索可能是最好的:)

概述:

  • 每组练习都会引导读者完成构建不同类型monad的步骤。这些步骤的概述如下:

    • 我们考虑两个函数 - f'g' - 它们具有相同的类型声明
    • 我们构造了一个函数(bind),它允许我们组合fg,使输出有意义(一旦你看到下面的具体练习,可能会更有意义)。直接组合不起作用,因为fg的返回类型与其参数类型不匹配。
    • 接下来,我们定义一个身份函数(unit),以便满足以下条件:
      (bind unit . f') == (bind f' . unit)
    • 根据上一步,我们定义lift函数,使lift f = unit . f
    • 在最后一步中,我们被要求表明以下内容成立:
      bind (lift f) (lift g) == lift . bind f g

以下是我正在进行的一系列练习:

  • 我们被要求考虑两个函数sqrt'cbrt',它们计算复数的平方根和立方根(即a + bi形式的数字,其中a和b是实数,而i是负面的平方根)。基础数学并不重要。重要的是,可能的第n个根的数量是n。换句话说,复数(即a + bi形式)具有复数的两个平方根,复数的三个立方根等。
  • 鉴于复杂根的性质,sqrt'cbrt'都采用Complex类型的参数并返回类型[Complex]。我们被要求构造一个bind函数,它允许我们计算复数的第六个根,同时利用我们已经有sqrt'和cbrt'。 (直接作文obv赢了工作)

    bind :: (Complex -> [Complex) -> [Complex] -> [Complex] bind f = (concat . map f)

  • 接下来,我们构建unitlift

    unit :: Complex -> [Complex] unit x = [x]

    lift :: (Complex -> [Complex]) -> Complex -> [Complex] lift f = unit . f

  • 在最后一步(这就是我遇到问题),我们要求显示以下内容:

    bind (lift f) (lift g) == lift . bind f g

首先,这个等式的左边是不是类型不匹配,因为lift不能接受[Complex]类型的参数?退后一步,我不确定为什么我们甚至不愿意定义unitlift。 (我天真的自我认为bind的定义解决了手头的问题,从而解决了下一个问题。)如果有人能帮助我理解为什么我们定义这两个函数然后寻求,我会非常感激。证明了最后的平等。


作为参考,我在下面附上我的代码。请注意,bindunitlift函数具有广义类型声明。

bind :: (a1 -> [a]) -> [a1] -> [a]
bind f' = (concat . map f')

unit :: t -> [t]
unit x = [x]

lift :: (a -> b) -> a -> [b]
lift f = unit . f

--Definitions of cbRootC and sqRootC
data Complex = Complex Float Float deriving (Show)

cbrt' = rootC 3
sqrt' = rootC 2

rootC :: Float -> Complex -> [Complex]
rootC n (Complex a b) = zipWith Complex r i
            where r = map (* (mod ** (1/n) )) $ map cos $ map arg [0..n-1]
                  i = map (* (mod ** (1/n) )) $ map sin $ map arg [0..n-1]
                  arg  = ( * (2*pi / n) )
                  mod = sqrt (a*a + b*b)

1 个答案:

答案 0 :(得分:2)

你的第一个误解是lift的类型。在问题描述中,您将其列为以下第一个,但在您的代码中,您将获得第二个。

lift :: (Complex -> [Complex]) -> Complex -> [Complex]
lift :: (a       -> b        ) -> a       -> [b]

注意第二个定义如何在第一个参数的返回类型周围包含[]。第二个是正确的。 lift将采用普通函数Complex -> Complex并生成一个"多值"从Complex -> [Complex]计算,多个值只返回从普通函数返回的单个值。

您的第二个误解与sigfpe's article*.的使用方式有关。 *用于撰写"多值"计算在一起; f * g = bind f . g.用于普通函数组合。

你没有被要求出示

bind (lift f) (lift g) == lift . bind f g

你没有确定这是不正确的。我们来试试吧。 bind应用于两个参数返回[a]lift应用于单个参数会返回a1 -> [b]

bind (lift f) (lift g) == lift . bind f g
[a]                    ~  a1 -> [b]

aa1b无法选择列表[]和函数->具有相同的类型

相反,您需要显示以下内容。请注意不同的符号*.。我们将*替换为bind.

      lift f  *  lift g  == lift (f . g)
bind (lift f) . (lift g) == lift (f . g)

我将为您留下余下的练习。

unitlift有用的原因是它们允许您重复使用已有的普通类型的东西。 lift将普通函数转换为"多值"计算和unit将普通值转换为"多值"的结果。计算