【问题标题】:Why "fmap (replicate 3) Just" has a type of "a -> [Maybe a]", In Haskell为什么“fmap (replicate 3) Just”的类型是“a -> [Maybe a]”,在 Haskell 中
【发布时间】:2014-06-03 02:10:39
【问题描述】:

最近我通过Learn You a Haskell for Great Good在线学习Haskell。

我有两个问题:

  1. fmap (replicate 3) 的类型为 Functor f=> f a -> f [a]。为什么可以应用于Just?

  2. 此外,为什么fmap (replicate 3) Just 是a -> [Maybe a] 类型,而不是a -> Maybe [a] 类型?

【问题讨论】:

    标签: haskell types functor


    【解决方案1】:

    如果您意识到fmap-ing 是一个函数,而不是Maybe a 值,这很容易理解。 Just 的类型是a -> Maybe a,所以它属于(->) a 函子,而不是Maybe 函子。函数的Functor 实例看起来像

    instance Functor ((->) a) where
        fmap g f = g . f
    

    所以fmap 就变成了正常的函数组合!这意味着

    fmap (replicate 3) Just
    

    与

    相同
    replicate 3 . Just
    

    很明显的类型是a -> [Maybe a]


    一个更“类型代数”的解释是排列类型并替换,直到你不能再替换。让我们从我们的类型开始,但使用不同的变量名称以便于理解:

    fmap      :: Functor f => (a -> b) -> (f a -> f b)
    replicate :: Int -> c -> [c]
    Just      :: x -> Maybe x
    

    那么对于fmap (replicate 3):

         (replicate 3) :: c -> [c]
    fmap               :: (a -> b) -> (f a -> f b)
    

    所以

    (c -> [c]) ~ (a -> b)
    

    这意味着

    c   ~ a
    [c] ~ b
    b   ~ [a]
    

    所以代入:

    fmap (replicate 3) :: f c -> f [c]
    

    那么我们fmap-ing 结束的是Just,它的类型是

    Just :: x -> Maybe x
    

    可以改写成前缀形式

    Just :: (->) x (Maybe x)
    

    或者如果我们真的需要更多的括号

    Just :: ((->) x) (Maybe x)
    

    然后

                       Just :: ((->) x) (Maybe x)
    fmap (replicate 3)      :: f        c         -> f [c]
    

    这意味着

    ((->) x) (Maybe x) ~ f c
    (->) x  ~ f
    Maybe x ~ c
    [c] ~ [Maybe x]
    

    所以代入:

    fmap (replicate 3) :: ((->) x) (Maybe x) -> ((->) x) [Maybe x]
    

    回到中缀符号

    fmap (replicate 3) :: (x -> Maybe x) -> (x -> [Maybe x])
    

    然后申请Just:

    fmap (replicate 3) Just :: x -> [Maybe x]
    

    我想在这里强调Maybe 是Functor 与此减少无关,唯一涉及的Functor 是函数Functor。列表也是Functor,但仅仅因为它出现在replicate 的类型中并不意味着它在这种情况下很重要。函数很容易混淆

    fmap (replicate 3) . Just :: a -> Maybe [a]
    

    但由于添加了.,这完全不同。

    【讨论】:

    • 非常感谢。我有点明白这一点。但我还有另一个问题。 “fmap (replicate 3)” 类型是 Functor f=> f a -> f [a],这意味着 fmap (repicate 3) 应该采用具体类型并返回具体类型。 “Just”是一个具体类型,或者换句话说,“a->Maybe a”是一个具体类型吗?
    • @Guocheng 我会向您推荐the wiki,它将具体数据类型定义为本质上没有类型变量的数据类型。您确定“具体类型”是您想要的术语吗?
    • 是的,我想是的。我可以很容易地理解像“fmap (replicate 3) (Just 3)”这样的表达式,因为 (Just 3) 是一个具体的类型,并且与 fmap 的类型描述相匹配。令我困惑的另一件事是:如果 [Maybe a] 的类型为“f [a]”,那么这里的“f”是什么?
    • @Guocheng 你有点糊涂了。我会在上面写一个更好的解释来展示类型是如何排列的,先给我几分钟=)
    • @Guocheng 我认为你所说的“具体类型”是我们在技术上用 kind * 称呼的东西(“kinds”基本上是“类型的类型”,而那种名为 @ 987654360@ 是一种具有值的普通类型;与 Maybe 之类的 * -> * 不同,它需要一个类型级参数来创建类似于 Maybe Int 之类的 * 的东西)。 Just 本身是 类型为*。 Just的类型为a -> Maybe a;它的值是函数。没有 (->) a (Maybe a) 的中缀符号。 (->) a 部分与 f a -> f [a] 中的 f 匹配。
    【解决方案2】:

    fmap 使用的Functor 实例是(->) r,而不是Maybe。让我们看看类型:

    replicate 3        :: a -> [a]
    fmap (replicate 3) :: Functor f => f a -> f [a]
    Just               :: b -> Maybe b
    
    1. 在表达式fmap (replicate 3) Just 中,我们需要将Functor f => f a 部分与b -> Maybe b 匹配。这里选择的实例是(->) r 实例(因为Just 是一个函数),我们最终得到((->) r a) -> ((->) r [a])。
    2. 我们可以扩展这些(->) 应用程序以获得这种类型(专门将函数作为其参数):(r -> a) -> (r -> [a])。
    3. 接下来,为了匹配Just的类型,我们需要将r与b和a与Maybe b匹配(这样我们就可以将(r -> a)与(b -> Maybe b)匹配)给出我们(b -> Maybe b) -> (b -> [Maybe b])。
    4. 这是fmap (replicate 3)的特殊类型,我们现在可以申请Just得到fmap (replicate 3) Just :: b -> [Maybe b],与a -> [Maybe a]相同。

    附带说明,fmap 专用于 (->) r 实例的完整类型是 fmap :: (a -> b) -> (r -> a) -> (r -> b),这意味着它与函数组合相同:fmap = (.)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多