如何表达`ap zip tail`

时间:2013-10-04 13:03:18

标签: haskell types pointfree

我想知道如何免费编写f x = zip x (tail x)。所以我使用了pointfree程序,结果是f = ap zip tailap是Control.Monad的一个函数

我不明白点免费定义是如何工作的。如果我能从类型的角度理解它,我希望我能弄明白。

import Control.Monad (ap)
let f = ap zip tail
let g = ap zip
:info ap zip tail f g
ap :: Monad m => m (a -> b) -> m a -> m b
    -- Defined in `Control.Monad'
zip :: [a] -> [b] -> [(a, b)]   -- Defined in `GHC.List'
tail :: [a] -> [a]      -- Defined in `GHC.List'
f :: [b] -> [(b, b)]    -- Defined at <interactive>:3:5
g :: ([a] -> [b]) -> [a] -> [(a, b)]
    -- Defined at <interactive>:4:5

通过查看表达式ap zip tail,我认为zip是ap的第一个参数,tail是ap的第二个参数。

Monad m => m (a -> b) -> m a -> m b
           \--------/   \---/
              zip        tail

但这是不可能的,因为ziptail的类型与函数ap所需的类型完全不同。即使考虑到该列表是一种单一的行列。

2 个答案:

答案 0 :(得分:11)

因此ap的类型签名为Monad m => m (a -> b) -> m a -> m b。你已经给它ziptail作为参数,所以让我们看一下它们的类型签名。

tail :: [a] -> [a] ~ (->) [a] [a]开始(此处~是类型的相等运算符),如果我们将此类型与ap的第二个参数的类型进行比较,

 (->) [x]  [x] ~ m a
((->) [x]) [x] ~ m a

我们得到a ~ [x]m ~ ((->) [x]) ~ ((->) a)。我们已经看到我们所在的monad是(->) [x],而不是[]。如果我们将{8}的类型签名替换为ap我们得到:

(((->) [x]) ([x] -> b)) -> (((->) [x]) [x]) -> (((->) [x]) b)

由于这不是非常易读,因此通常可以将其写为

  ([x] -> ([x] -> b)) -> ([x] -> [x]) -> ([x] -> b)
~ ([x] ->  [x] -> b ) -> ([x] -> [x]) -> ([x] -> b)

zip的类型为[x] -> [y] -> [(x, y)]。我们已经可以看到这与ap的第一个参数

对齐
[x]         ~    [x]   
[y]         ~    [x]   
[(x, y)]    ~    b

这里我列出了垂直类型,以便您可以轻松查看哪些类型排列。很明显x ~ xy ~ x[(x, y)] ~ [(x, x)] ~ b,所以我们可以完成将b ~ [(x, x)]替换为ap的类型签名并获取

([x] -> [x] -> [(x, x)]) -> ([x] -> [x]) -> ([x] -> [(x, x)])
--   zip                        tail        ( ap  zip  tail )
--                                            ap  zip  tail u = zip u (tail u)

我希望能为你解决问题。

编辑:评论中的danvari pointed out,monad (->) a有时被称为读者monad。

答案 1 :(得分:6)

理解这一点有两个方面:

  1. 魔法类型
  2. 实施的信息流程
  3. 首先,这有助于我理解类型魔术

    1) zip          : [a] → ( [a] → [(a,a)] )
    2) tail         : [a] → [a]
    3) zip <*> tail : [a] → [(a,a)]
    
    4) <*> : Applicative f ⇒ f (p → q) → f p → f q
    

    在这种情况下,对于<*>

    5) f x = y → x
    

    请注意,在5中,f是一个类型构造函数。将f应用于x会生成一种类型。此外,此处=被重载以表示类型的等价性。

    y目前是占位符,在这种情况下,它是[a],这意味着

    6) f x = [a] -> x
    

    使用6,我们可以按如下方式重写1,2和3:

    7) zip          : f ([a] → [(a,a)])
    8) tail         : f [a]
    9) zip <*> tail : f ([a] → [(a,a)])  →  f [a]  →  f [(a,a)]
    

    所以,看看4,我们代替如下:

    10) p = [a]
    11) q = [(a,a)]
    12) f x =  [a] → x
    

    (6再次重复为12)

    其次,信息流,即实际功能。这更容易,从<*>的{​​{1}}的定义可以清楚地看出,Applicative instance of y →使用不同的标识符名称并使用中缀样式重写:

    13) g <*> h $ xs = g xs (h xs)
    

    代替如下:

    14) g = zip
    15) h = tail
    

    给出:

    zip <*> tail $ xs        (Using 14 and 15)
      ==
    zip xs (tail xs)         (Using 13 )