导出Haskell类型和(< *>)(< *>)的实现

时间:2014-12-22 12:56:14

标签: haskell types functional-programming

我是新手,刚开始学习Haskell,所以如果我提出愚蠢的问题,请耐心等待。

最近我遇到了SO中的问题,演示了如何推导出函数的类型和实现。表达(问题,如

How can I understand "(.) . (.)"?

&安培;

Haskell function composition, type of (.)(.) and how it's presented

我发现答案非常鼓舞人心

然后,我尝试为自己做一些练习,以确保我知道如何应用这些技巧。

然后我自己提出这个表达:(<*>)(<*>)我不知道如何解决。

在GHCi中,它将类型签名赋予:

(<*>)(<*>) :: Applicative f => (f (a -> b) -> f a) -> f (a -> b) -> f b

但我的问题是如何从

开始
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

并导出GHCi给出的类型签名?

此外,根据类型签名,(<*>)(<*>) = ??的实现方式如何?

我陷入困境,无法通过重新安排条款等方法来解决这个问题。我甚至不知道从哪里开始。

有人会帮我一个忙吗?

非常感谢

注意**:表达式(<*>)(<*>)确实没有特殊意义,它只是我随机为自己做的一个练习

4 个答案:

答案 0 :(得分:8)

首先,让我们用两个不相交的类型变量集写出(<*>)的类型:

(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
(<*>) :: (Applicative g) => g (c -> d) -> g c -> g d

如果我们想要使用第一个作为参数的第二个,我们需要

g (c -> d) ~ f (a -> b) -> f a -> f b

产生

g ~ (f (a -> b) ->)    or,   ((->) (f (a -> b)) )
c ~ f a
d ~ f b

并注意g的选择确实是一个适用的仿函数:它是非新颖的Reader (f (a -> b))应用程序。

所以应用程序有类型

g c -> g d

我们现在知道

(f (a -> b) -> f a) -> (f (a -> b) -> f b)

q.e.d。

答案 1 :(得分:3)

所以我们有了这个功能

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

并向其应用<*> :: Applicative g => g (c -> d) -> g c -> g d(我替换了类型变量,因为<*>的内部和外部(<*>)(<*>)具有不同的含义。如果我们将第二个<*>应用于第一个<*> <*>,然后第二个f (a -> b)必须是g (c -> d) -> g c -> g d = f (a -> b) 类型。所以我们在类型上有以下等式:

g (c -> d) -> g c -> g d

但是(->) (g (c -> d)) (g c -> g d)(->) (g (c -> d)) (g c -> g d) = f (a -> b) 的语法糖。所以:

(->) (g (c -> d)) = f

两边的类型构造函数和参数类型必须相等,所以:

(g c -> g d) = (a -> b)

a = g c

表示b = g d(<*>)

现在我们可以看到第一个f a -> f b = ((->) (g (c -> d)) (g c)) -> ((->) (g (c -> d)) (g d)) = (g (c -> d) -> g c) -> (g (c -> d) -> g d) 的返回值是

(f (a -> b) -> f a) -> f (a -> b) -> f b

(->) (g (c -> d))模数重命名变量相同。

此示例中棘手的部分是(->) e是适用的(与任何其他{{1}}相同)。

答案 2 :(得分:3)

  

此外,基于类型签名,如何执行   (<*>)(<*>) = ??会是什么?

更容易颠倒顺序(首先找到实现,然后派生它的类型)。

功能的应用实例是

instance Applicative ((->) a) where
    pure = const
    (<*>) f g x = f x (g x)

因此,只需在上述定义中用<*>代替f(<*>) (<*>)就是\g x -> x <*> g x。或者在alpha转换后\g h -> h <*> g h

由于(<*>)的第一个参数是h,因此某些h :: f (a -> b)a的{​​{1}}类型必须为b,其中fApplicative

由于g收到h作为参数,因此对于某些g :: f (a -> b) -> c,它必须是c类型。

由于(<*>)的第一个参数h的类型为f (a -> b),而(<*>)的第二个参数为g h,因此其类型必须为{{ 1}},由于事实,

g h :: f a

(<*>) :: f (a -> b) -> f a -> f b g :: f (a -> b) -> c

以来
g h :: f a

由于h :: f (a -> b) g :: f (a -> b) -> c g h :: f a , c ~ f a (!) 的类型为hf (a -> b)的类型为h <*> (whatever :: f a)

总而言之:

f b

所以我们有

h         :: f (a -> b)
g         :: f (a -> b) -> f a
h <*> g h :: f b

答案 3 :(得分:3)

作为练习,将其缩小为函数,其中<*>只是 S -combinator,

Prelude> let s f g x = f x (g x)

Prelude> :t s s
s s :: ((t -> t1 -> t2) -> t -> t1) -> (t -> t1 -> t2) -> t -> t2
--               g2          g4                 g2          g1
--         g3                             g3
--                      g5
--                                  g6

重要的是要知道类型中的箭头与右侧相关联,因此((t -> t1 -> t2) -> t -> t1)实际上是((t -> t1 -> t2) -> (t -> t1)),而(t -> t1 -> t2)(t -> (t1 -> t2))

实施:

g6 g5 g3 t :: t2 
  g3 t t1 :: t2          -- need t1
  g5 g3 t :: t1
  g3 t (g5 g3 t) :: t2   -- so, 

h f g x = g x (f g x)    

所以,当我们有一个类型时,它只是连接电线可以这么说,或者将乐高积木放在一起。我们尝试查看给定实体(参数)的哪些组合具有哪种类型,如果它们对于获得所需的输出类型(t2,以上)有用,则可以在其他组合中使用它们。

类似地,

Prelude Control.Applicative> :t (<*>) (<*>)
(<*>) (<*>) :: (Applicative f) =>
               (f (a -> b) -> f a) -> f (a -> b) -> f b
--                         f          fg
Prelude Control.Applicative> :t let axa f fg = fg <*> f fg in axa
let axa f fg = fg <*> (f fg) in axa :: (Applicative f) =>
                                       (f (a -> b) -> f a) -> f (a -> b) -> f b