Function 如何从类型签名实现函数?

Function 如何从类型签名实现函数?,function,haskell,types,functional-programming,implementation,Function,Haskell,Types,Functional Programming,Implementation,我在Haskell中有以下两种类型的签名: foo :: (a -> (a,b)) -> a -> [b] bar :: (a -> b) -> (a -> b -> c) -> a -> c 我想写这两个函数的具体实现,但我真的很难理解从哪里开始 我知道foo接受函数(a->(a,b))并返回a和包含b的列表 而bar接受一个函数(b->c),该函数返回一个函数(a->b->c),该函数最终返回a和c 有人能给我举一个具体实现的例子吗

我在Haskell中有以下两种类型的签名:

foo :: (a -> (a,b)) -> a -> [b]

bar :: (a -> b) -> (a -> b -> c) -> a -> c
我想写这两个函数的具体实现,但我真的很难理解从哪里开始

我知道
foo
接受函数
(a->(a,b))
并返回
a
和包含
b
的列表

bar
接受一个函数
(b->c)
,该函数返回一个函数
(a->b->c)
,该函数最终返回
a
c

有人能给我举一个具体实现的例子吗


我如何知道从何处开始,以及定义的左侧是什么?

您有一些误解:

我知道
foo
接受函数
(a->(a,b))
并返回
a
和包含
b
的列表

不,它不会返回
a
。它期望它作为该函数之外的另一个参数

bar
接受一个函数
(b->c)
,该函数返回一个函数
(a->b->c)
,该函数最终返回
a
c

这里也一样。给定
g::a->b
bar
返回函数
bar g:(a->b->c)->a->c
。这个函数,反过来,给定一个函数
h::(a->b->c)
,返回一个类型为
a->c
的函数。事情就是这样


就像玩拼图一样:

foo :: (a -> (a,b)) -> a -> [b]
--   g   :: a -> (a,b)
--     x :: a
--   g x ::      (a,b)
foo g x = [b]  where
  (a,b) = g x

bar :: (a -> b) -> (a -> b -> c) -> a -> c
--   g   :: a -> b
--     x :: a
--   g x ::      b
--   h   :: a -> b -> c
--   h x ::      b -> c
--   h x (g x)     :: c
bar g h x = c  where
  c = ....
我们在这里没有多少自由选择。尽管如此,对于
foo
,有更多的方法可以获取更多类型
b
的值。与其忽略
(a,b)=gx
中的
a
,我们可以在
g
的更多应用中使用它,因此实际上有更多的可能性,比如

foo2 :: (a -> (a,b)) -> a -> [b]
foo2 g x = [b1,b2]  where
  (a1,b1) = g x
  (a2,b2) = g a1
还有更多。尽管如此,类型还是指导了可能的实现
foo
甚至可以根据以下类型在其实现中使用
foo

foo3 :: (a -> (a,b)) -> a -> [b]
foo3 g x = b : bs  where
  (a,b) = g x
  bs = ...

所以现在,在这个实现中,前两个成为它的特例:
foogx==take1(foo3gx)
foo2gx==take2(foo3gx)
。有最一般的定义可能是最好的。

你有一些误解:

我知道
foo
接受函数
(a->(a,b))
并返回
a
和包含
b
的列表

不,它不会返回
a
。它期望它作为该函数之外的另一个参数

bar
接受一个函数
(b->c)
,该函数返回一个函数
(a->b->c)
,该函数最终返回
a
c

这里也一样。给定
g::a->b
bar
返回函数
bar g:(a->b->c)->a->c
。这个函数,反过来,给定一个函数
h::(a->b->c)
,返回一个类型为
a->c
的函数。事情就是这样


就像玩拼图一样:

foo :: (a -> (a,b)) -> a -> [b]
--   g   :: a -> (a,b)
--     x :: a
--   g x ::      (a,b)
foo g x = [b]  where
  (a,b) = g x

bar :: (a -> b) -> (a -> b -> c) -> a -> c
--   g   :: a -> b
--     x :: a
--   g x ::      b
--   h   :: a -> b -> c
--   h x ::      b -> c
--   h x (g x)     :: c
bar g h x = c  where
  c = ....
我们在这里没有多少自由选择。尽管如此,对于
foo
,有更多的方法可以获取更多类型
b
的值。与其忽略
(a,b)=gx
中的
a
,我们可以在
g
的更多应用中使用它,因此实际上有更多的可能性,比如

foo2 :: (a -> (a,b)) -> a -> [b]
foo2 g x = [b1,b2]  where
  (a1,b1) = g x
  (a2,b2) = g a1
还有更多。尽管如此,类型还是指导了可能的实现
foo
甚至可以根据以下类型在其实现中使用
foo

foo3 :: (a -> (a,b)) -> a -> [b]
foo3 g x = b : bs  where
  (a,b) = g x
  bs = ...

所以现在,在这个实现中,前两个成为它的特例:
foogx==take1(foo3gx)
foo2gx==take2(foo3gx)
。拥有最一般的定义可能是最好的。

除了@will-nes的答案外,将(->)视为右关联中缀运算符也很有用。所以类似于
f:a->b->c
的东西与
f:a->(b->c)
是一样的。也就是说,
f
是一个函数,它接受类型为
a
的值,并返回类型为
b->c
的值,这是另一个函数,它接受类型为
b
的值,并返回类型为
c
的值

因此,可以按如下方式重新编写示例中的类型

foo :: (a -> (a,b)) -> (a -> [b])

bar :: (a -> b) -> ((a -> (b -> c)) -> (a -> c))
类似地,您也可以将函数的参数看作是分段的,例如左关联(如+和-),尽管在这种情况下没有显式运算符
fooabcdde
(((fooa)b)c)d)e
相同。例如,假设我们有一个函数
f:Int->Int->Int
(与
f:Int->(Int->Int)
相同)。您不必同时提供两个参数。因此,您可以编写
g=f1
,其类型为
(Int->Int)
。然后您可以为
g
提供一个参数,比如
g2
,它的类型为
Int
F12
让G2中的g=F1
大致相同。下面是一个更具体的例子,说明这是如何工作的:

Prelude> f = (+)
Prelude> g = f 1
Prelude> g 2
3
Prelude> :t f
f :: Num a => a -> a -> a
Prelude> :t g
g :: Num a => a -> a
Prelude> :t g 2
g 2 :: Num a => a
在@will-nes的示例实现示例中,他使用前面的所有参数定义函数,但您不必这样想。只需将
f:a->b->c
想象为获取类型为
a
的值并返回另一个函数。虽然您遇到的大多数方法都会预先使用它们的所有参数,但在某些情况下,您可能不想这样做。下面是一个例子:

veryExpensive :: A -> B

unstagedFun :: A -> (B -> C) -> C
unstagedFun a f = f (veryExpensive a)

stagedFun :: A -> (B -> C) -> C
stagedFun a = let b = veryExpensive a in \f -> f b
(您也可以将后者重写为
让b=veryExpensive a in($b)

当然,对于编译器优化,如果未老化的版本自动升级,我不会感到惊讶,但希望这能提供一些动机,让人们认为函数没有多个参数,而是作为一个参数,但它们可能会返回其他函数,而这些函数本身可能会返回函数(但也只需要一个s)