我是新手,刚开始学习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给出的类型签名?
此外,根据类型签名,(<*>)(<*>) = ??
的实现方式如何?
我陷入困境,无法通过重新安排条款等方法来解决这个问题。我甚至不知道从哪里开始。
有人会帮我一个忙吗?
非常感谢
注意**:表达式(<*>)(<*>)
确实没有特殊意义,它只是我随机为自己做的一个练习
答案 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
,其中f
在Applicative
。
由于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 (!)
的类型为h
,f (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